module BattleshipBoard
Constants
- COLS
- HIT_MARK
- MISS_MARK
- SHIPS
- SHIP_MARK
- SIZE
- UNKNOWN
- WATER
Public Instance Methods
Source
# File lib/battleship_board.rb, line 136 def all_sunk?(fleet) fleet["ships"].all? { |ship| ship["sunk"] } end
Source
# File lib/battleship_board.rb, line 56 def build_random_placements(rand:) placements = [] occupied = {} SHIPS.each do |ship| options = [] SIZE.times do |row| SIZE.times do |col| [true, false].each do |horizontal| cells = cells_for(row, col, ship[:length], horizontal) next unless cells next unless cells.all? { |idx| !occupied[idx] } options << { name: ship[:name], length: ship[:length], cells: cells } end end end raise "No space for #{ship[:name]}" if options.empty? choice = options[rand.call(options.length)] choice[:cells].each { |idx| occupied[idx] = true } placements << choice end placements end
Source
# File lib/battleship_board.rb, line 81 def cells_for(row, col, length, horizontal) cells = [] length.times do |i| r = horizontal ? row : row + i c = horizontal ? col + i : col return nil unless r.between?(0, SIZE - 1) && c.between?(0, SIZE - 1) cells << (r * SIZE + c) end cells end
Source
# File lib/battleship_board.rb, line 22 def coord_to_index(coord) match = coord.to_s.strip.match(/\A([A-J])(10|[1-9])\z/i) return nil unless match col = match[1].upcase.ord - "A".ord row = match[2].to_i - 1 return nil unless row.between?(0, SIZE - 1) && col.between?(0, SIZE - 1) row * SIZE + col end
Source
# File lib/battleship_board.rb, line 108 def fire(fleet:, index:) return { error: "Invalid coordinate." } if index.nil? return { error: "Already fired here." } if fleet["hits"].include?(index) fleet = fleet.dup hits = fleet["hits"].dup hits << index fleet["hits"] = hits ship_id = fleet["ocean"][index] if ship_id.nil? return { fleet: fleet, result: :miss, sunk: false } end ships = fleet["ships"].map(&:dup) ship = ships[ship_id] sunk = ship["cells"].all? { |cell| hits.include?(cell) } ship["sunk"] = true if sunk fleet["ships"] = ships { fleet: fleet, result: sunk ? :sunk : :hit, sunk: sunk, ship_name: ship["name"] } end
Source
# File lib/battleship_board.rb, line 33 def index_to_coord(index) row = index / SIZE col = index % SIZE "#{COLS[col]}#{row + 1}" end
Source
# File lib/battleship_board.rb, line 39 def neighbors(index) row = index / SIZE col = index % SIZE deltas = [[-1, 0], [1, 0], [0, -1], [0, 1]] deltas.filter_map do |dr, dc| nr = row + dr nc = col + dc next unless nr.between?(0, SIZE - 1) && nc.between?(0, SIZE - 1) nr * SIZE + nc end end
Source
# File lib/battleship_board.rb, line 93 def place_fleet_at(placements) ocean = Array.new(SIZE * SIZE) ships = placements.each_with_index.map do |placement, id| placement[:cells].each { |idx| ocean[idx] = id } { "id" => id, "name" => placement[:name], "length" => placement[:length], "cells" => placement[:cells], "sunk" => false } end { "ocean" => ocean, "ships" => ships, "hits" => [] } end
Source
# File lib/battleship_board.rb, line 52 def place_fleet_random(rand: ->(max) { Random.rand(0...max) }) place_fleet_at(build_random_placements(rand: rand)) end
Source
# File lib/battleship_board.rb, line 166 def render_grid(_fleet = nil) header = " #{COLS.join(' ')}" rows = [header] SIZE.times do |row| cells = (0...SIZE).map do |col| index = row * SIZE + col if block_given? yield(index) else WATER end end rows << format("%2d %s", row + 1, cells.join(" ")) end rows.join("\n") end
Source
# File lib/battleship_board.rb, line 140 def render_own(fleet) render_grid(fleet) do |index| if fleet["hits"].include?(index) HIT_MARK elsif fleet["ocean"][index] SHIP_MARK else WATER end end end
Source
# File lib/battleship_board.rb, line 152 def render_target(shots) render_grid do |index| coord = index_to_coord(index) case shots[coord] when :hit, :sunk HIT_MARK when :miss MISS_MARK else UNKNOWN end end end