-
-
Save nithinbekal/719275d69f4c6689121d to your computer and use it in GitHub Desktop.
RPCFN - Broadsides
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This program implements a player for the Broadsides game as part of the | |
# Ruby Programming Challenge for Newbies (RPCFN #7). | |
# | |
# Author:: Nithin Bekal | |
# This class represents the 10x10 board used in this challenge. The variable | |
# @board holds the current status of all the tiles on the board. The statuses | |
# are described below: | |
# | |
# * . - The status of the tile is unknown. | |
# * h - The target has already been hit. | |
# * m - The target has been shot at but was empty. | |
# * q - The target is a neighbor to an already hit target. | |
class Board | |
attr_accessor :board | |
def initialize | |
@board = [ | |
['.','.','.','.','.','.','.','.','.','.',], | |
['.','.','.','.','.','.','.','.','.','.',], | |
['.','.','.','.','.','.','.','.','.','.',], | |
['.','.','.','.','.','.','.','.','.','.',], | |
['.','.','.','.','.','.','.','.','.','.',], | |
['.','.','.','.','.','.','.','.','.','.',], | |
['.','.','.','.','.','.','.','.','.','.',], | |
['.','.','.','.','.','.','.','.','.','.',], | |
['.','.','.','.','.','.','.','.','.','.',], | |
['.','.','.','.','.','.','.','.','.','.',], | |
] | |
end | |
def to_s | |
s = " A B C D E F G H I J \n" | |
(1..10).each do |x| | |
s += "%5s" %(x.to_s + ' ') | |
("A".."J").each do |y| | |
s += "#{get_status("#{y}#{x}")} " | |
end | |
s += "\n" | |
end | |
s | |
end | |
# Returns status of the tile. | |
def get_status pos | |
y = pos[0] - 65 | |
x = pos[1,2].to_i - 1 | |
board[x][y] | |
end | |
# Sets the status of the specified tile. | |
def set_status pos, status | |
y = pos[0] - 65 | |
x = pos[1,2].to_i - 1 | |
board[x][y] = status | |
end | |
# Returns an array of targets that have already been hit. | |
def hit_targets | |
hits = [] | |
("A".."J").each do |x| | |
(1..10).each do |y| | |
hits.push "#{x}#{y}" if is_hit? "#{x}#{y}" | |
end | |
end | |
hits | |
end | |
# Returns an empty tile. | |
def empty_tiles | |
tiles = [] | |
("A".."J").each { |x| (1..10).each { |y| tiles.push "#{x}#{y}" if is_empty? "#{x}#{y}" } } | |
tiles.sort_by { rand } | |
end | |
# Returns array of targets. Neighbors of hit targets are queued | |
# first, then other targets in random order. | |
def get_new_targets | |
targets = [] | |
hit_targets.each do |t| | |
targets.push neighbors(t) | |
neighbors(t).each { |n| set_status(n, 'q') } | |
end | |
targets.push(empty_tiles).flatten | |
end | |
# Returns true if the tile's status is unknown. | |
def is_empty? pos | |
get_status(pos)== '.' ? true : false | |
end | |
# Returns true if the tile has already been hit. | |
def is_hit? pos | |
get_status(pos)== 'h' ? true : false | |
end | |
# Returns the tiles to the top, left, right and bottom of a given tile. | |
def neighbors pos | |
x = pos[0,1] | |
y = pos[1,2] | |
neighbors_list = [] | |
unless y == "1" | |
top = "#{x}#{y.to_i-1}" | |
neighbors_list.push top unless ['h', 'm'].include? get_status(top) | |
end | |
unless y == "10" | |
bottom = "#{x}#{y.to_i+1}" | |
neighbors_list.push bottom unless ['h', 'm'].include? get_status(bottom) | |
end | |
unless x == "J" | |
right = "#{x.next}#{y}" | |
neighbors_list.push right unless ['h', 'm'].include? get_status(right) | |
end | |
unless x == "A" | |
left = "#{(x[0]-1).chr}#{y}" | |
neighbors_list.push left unless ['h', 'm'].include? get_status(left) | |
end | |
neighbors_list | |
end | |
# Uses the string returned by the game to set the status of the tiles that | |
# were most recently shot at. | |
def update_hits_and_misses info | |
shots = info.split | |
shots.each do |s| | |
loc = s.split(':')[0] | |
status = s.split(':')[1][0].chr | |
self.set_status loc, status | |
end | |
end | |
end | |
# The board is divided into 5 blocks and the ships are placed, one in each | |
# block. The bigger ships are placed further away from the position A1 and the | |
# shorter ones are closer. Two of the shorter ones are placed vertically and | |
# the rest horizontal. | |
def place_ships_randomly | |
str = "5:" + (rand(6) + 97).chr.upcase + (rand(3) + 8).to_s + ":H " | |
str += "4:" + (rand(2) + 102).chr.upcase + (rand(3) + 5).to_s + ":H " | |
str += "3:" + (rand(3) + 102).chr.upcase + (rand(4) + 1).to_s + ":H " | |
str += "3:" + (rand(5) + 97).chr.upcase + (rand(2) + 4).to_s + ":V " | |
str += "2:" + (rand(5) + 97).chr.upcase + (rand(2) + 1).to_s + ":V" | |
end | |
$stdout.sync = true | |
b = Board.new | |
ARGF.each_line do |line| | |
case line | |
when /\AACTION SHIPS\b/ | |
# Call the method to get random position of the ships within the blocks | |
# defined for each of them | |
puts "SHIPS " + place_ships_randomly | |
when /\AACTION SHOTS (\d)/ | |
# Fetch new targets to hit by ordering the nighboring tiles of hit targets | |
# first use random targets if there aren't enough likely targets. | |
shots = b.get_new_targets | |
targets = (1..$1.to_i).map { | |
target = shots.shift | |
shots.push(target) | |
target | |
} | |
puts "SHOTS #{targets[0..$1.to_i].join(' ')}" | |
when /\AINFO SHOTS nithin\b/ | |
# Update the information about which tiles on the board have been hit | |
# or missed. | |
shot_status = line | |
shot_status["INFO SHOTS nithin "] = '' | |
b.update_hits_and_misses shot_status | |
when /\AACTION FINISH\b/ | |
# Finish the game. No saving of gama data. | |
puts "FINISH" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment