public
Created

  • Download Gist
OllyPlayer.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
require 'set'
require 'matrix'
 
class OllyPlayer
BoardSize = 10
Infinity = (1.0/0)
 
class Board
def initialize(current_state, ships_remaining)
row = Array.new(BoardSize, 0)
@board = Array.new(BoardSize) { row.dup }
calculate_scores(current_state, ships_remaining)
end
 
def next_target
highest_scoring_coordinates.shuffle.first
end
 
protected
def [](x, y)
if in_bounds?(x, y)
@board[x][y]
else
0
end
end
 
def []=(x, y, score)
if in_bounds?(x, y)
@board[x][y] = score
end
end
 
def coordinates_by_score
coordinates_by_score = {}
@board.each.with_index do |row, y|
row.each.with_index do |score, x|
coordinates_by_score[score] ||= []
coordinates_by_score[score] << [x, y]
end
end
coordinates_by_score
end
 
def highest_scoring_coordinates
highest_scoring_coordinates = coordinates_by_score
highest_scoring_coordinates[highest_scoring_coordinates.keys.sort.last]
end
 
private
def calculate_scores(current_state, ships_remaining)
current_state.each.with_index do |row, x|
row.each.with_index do |state, y|
case state
when :miss
self[x, y] = -Infinity
when :hit
self[x, y] = -Infinity
self[x, y - 1] += 10
self[x, y + 1] += 10
self[x - 1, y] += 10
self[x + 1, y] += 10
when :unknown
ships_remaining.uniq!
ships_remaining.each do |ship_size|
if ((x - y) % ship_size == 0)
self[x, y] += 1
end
end
end
end
end
end
def in_bounds?(x, y)
bounds = (0...BoardSize)
bounds.include?(x) && bounds.include?(y)
end
end
 
class Region
def self.coordinates(top_left, bottom_right)
xs, ys = top_left.zip(bottom_right)
 
cells = Set.new
Range.new(*xs).each do |x|
Range.new(*ys).each do |y|
cells << Vector[x, y]
end
end
new(cells)
end
 
def initialize(cells)
@cells = Set.new(cells)
end
 
attr_reader :cells
 
def -(region)
Region.new(self.cells - region.cells)
end
 
def can_place?(size)
placements(size).any?
end
 
def placement(size)
placements(size).to_a.shuffle.first
end
 
private
def placements(size)
positions = {
across: Vector[size - 1, 0],
down: Vector[0, size - 1]
}
 
Enumerator.new do |yielder|
cells.each do |start_cell|
positions.each do |position, end_transform|
if cells.include?(start_cell + end_transform)
yielder.yield [*start_cell, size, position]
end
end
end
end
end
end
 
def name
"Olly Legg"
end
 
def new_game
regions = []
center = Region.coordinates([3, 3], [6, 6])
regions << center
regions << Region.coordinates([0, 0], [4, 4]) - Region.new([[0, 0]]) - center # Top Left
regions << Region.coordinates([5, 0], [9, 4]) - Region.new([[9, 9]]) - center # Top Right
regions << Region.coordinates([0, 5], [4, 9]) - Region.new([[0, 9]]) - center # Bottom Left
regions << Region.coordinates([5, 5], [9, 9]) - Region.new([[9, 0]]) - center # Bottom Right
 
ship_sizes = [5, 4, 3, 3, 2]
ships = ship_sizes.map do |ship_size|
regions.shuffle
region = regions.detect {|region| region.can_place?(ship_size) }
regions.delete(region)
region.placement(ship_size)
end
 
return ships
end
 
def take_turn(state, ships_remaining)
board = Board.new(state, ships_remaining)
board.next_target
end
end

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.