Created
February 27, 2015 22:20
-
-
Save jkarmel/bba95aecf9c0b9b0f743 to your computer and use it in GitHub Desktop.
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
# #fire | |
# Takes: r (A-J),c (1-10) | |
# Returns: MISS, HIT, SUNK, WIN, ()DUPE) | |
# #place | |
# Takes: r (A-J),c (1-10), Ship object | |
# Returns success/failure as a boolean | |
class Board | |
ROWS = 'ABCDEFGHIJ'.split(//) | |
MAX_COLUMNS = 10 # 1..10, but we're zero-basing it | |
MISS=0 | |
HIT=1 | |
SUNK=2 | |
WIN=3 | |
DUPE=4 | |
def initialize | |
@board = {} | |
@ships = [] | |
end | |
def place(start_square, name, orientation) | |
ship = Ship.new(name) | |
c, r = get_coordinates(start_square) | |
on_board = on_board?(start_square, orientation, ship.length) | |
overlap = overlap?(start_square, orientation, ship.length, get_occupied_squares) | |
return false if !on_board || overlap | |
case orientation | |
when :vertical | |
(r .. r+ship.length - 1).each do |row| | |
@board[to_square(row, c)] = ship | |
end | |
else | |
(c .. c+ship.length - 1).each do |col| | |
@board[to_square(r, col)] = ship | |
end | |
end | |
@ships << ship | |
return true | |
end | |
def fire(target_square) | |
ship = @board[target_square] | |
case ship | |
when nil | |
MISS | |
@board[target_square] = MISS | |
when Ship | |
@board[target_square] = HIT | |
ship.hit | |
@ships.all?{|s| s.sunk?} ? WIN : (ship.sunk? ? SUNK : HIT) | |
else | |
DUPE | |
end | |
end | |
def on_board?(start_square, orientation, length) | |
c, r = get_coordinates(start_square) | |
case orientation | |
when :vertical | |
r+length <= ROWS.length | |
else | |
c+length <= MAX_COLUMNS | |
end | |
end | |
def get_coordinates(start_square) | |
r = ROWS.index(start_square[0]) | |
c = start_square[1..-1].to_i - 1 | |
return c, r | |
end | |
def overlap?(start_square, orientation, length, occupied_squares) | |
c, r = get_coordinates(start_square) | |
case orientation | |
when :vertical | |
(r .. r+length - 1).any? do |row| | |
occupied_squares.include?(to_square(row, c)) | |
end | |
else | |
(c .. c+length - 1).any? do |col| | |
occupied_squares.include? to_square(r, col) | |
end | |
end | |
end | |
def get_occupied_squares | |
@board.keys.select{|sq| @board[sq].is_a? Ship} | |
end | |
def to_square(r, c) | |
"#{ROWS[r]}#{c+1}" | |
end | |
end | |
class Ship | |
SHIPS = { | |
carrier: { | |
display_name: 'Carrier', | |
length: 5 | |
}, | |
battleship: { | |
display_name: 'BB', | |
length: 4 | |
}, | |
patrol: { | |
display_name: 'Patrol Boat', | |
length: 2 | |
} | |
} | |
def initialize(name) | |
@name = name.to_sym | |
@hits = 0 | |
end | |
def length | |
SHIPS[@name][:length] | |
end | |
def hit | |
@hits +=1 | |
end | |
def sunk? | |
@hits >= SHIPS[@name][:length] | |
end | |
end |
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
require './battleship' | |
RSpec.configure do |config| | |
config.mock_with :rr | |
end | |
describe Board do | |
let(:board) { Board.new } | |
describe '#place' do | |
it 'should place a ship' do | |
return_value = board.place('A1', :carrier, :vertical) | |
expect(return_value).to be true | |
end | |
it 'should not place a ship on a non-board position' do | |
return_value = board.place('I9', :carrier, :vertical) | |
expect(return_value).to be false | |
end | |
it 'should not place a ship on another ship position' do | |
board.place('B6', :carrier, :vertical) | |
return_value = board.place('C5', :patrol, :horizontal) | |
expect(return_value).to be false | |
end | |
end | |
describe '#on_board?' do | |
context 'when ship does not extend off the board' do | |
it 'should return true' do | |
# coordinates carefully chosen to show up fencepost errors | |
expect(board.on_board?('E1', :vertical, 5)).to be true | |
expect(board.on_board?('F1', :vertical, 5)).to be true | |
expect(board.on_board?('D5', :horizontal, 5)).to be true | |
expect(board.on_board?('D6', :horizontal, 5)).to be true | |
end | |
end | |
context 'when ship (just barely) extends off the board' do | |
it 'should return false' do | |
# coordinates carefully chosen to show up fencepost errors | |
expect(board.on_board?('G2', :vertical, 5)).to be false | |
expect(board.on_board?('D7', :horizontal, 5)).to be false | |
end | |
end | |
end | |
describe '#overlap?' do | |
context 'when there is no overlap' do | |
it 'should return false' do | |
expect(board.overlap?('B3', :vertical, 2, ['A3', 'B2', 'B4', 'C2', 'D3'])).to be_falsy | |
expect(board.overlap?('B3', :horizontal, 2, ['A3', 'B2', 'B5', 'C2', 'D3'])).to be_falsy | |
end | |
end | |
context 'when there is overlap' do | |
it 'should return true' do | |
expect(board.overlap?('B3', :vertical, 2, ['A3', 'C3', 'B4', 'C2', 'D3'])).to be_truthy | |
expect(board.overlap?('B3', :horizontal, 2, ['A3', 'B4', 'B5', 'C2', 'D3'])).to be_truthy | |
end | |
end | |
end | |
describe '#occupied_squares' do | |
it 'should return a list of occupied squares' do | |
board.place('B5', :carrier, :horizontal) | |
board.place('C6', :patrol, :vertical) | |
expect(board.get_occupied_squares).to eql(%w[B5 B6 B7 B8 B9 C6 D6]) | |
end | |
end | |
describe '#fire' do | |
before do | |
board.place('A3', :carrier, :horizontal) | |
board.place('B5', :patrol, :vertical) | |
end | |
context 'when you miss' do | |
it 'should return miss' do | |
expect(board.fire('A1')).to eql(Board::MISS) | |
end | |
end | |
context 'when you hit' do | |
it 'should return hit' do | |
expect(board.fire('A4')).to eql(Board::HIT) | |
end | |
end | |
context 'when you sink' do | |
it 'should return sink' do | |
expect(board.fire('B5')).to eql(Board::HIT) | |
expect(board.fire('C5')).to eql(Board::SUNK) | |
end | |
end | |
context 'when you win' do | |
it 'should return sink' do | |
expect(board.fire('A3')).to eql(Board::HIT) | |
expect(board.fire('A4')).to eql(Board::HIT) | |
expect(board.fire('A5')).to eql(Board::HIT) | |
expect(board.fire('A6')).to eql(Board::HIT) | |
expect(board.fire('A7')).to eql(Board::SUNK) | |
expect(board.fire('B5')).to eql(Board::HIT) | |
expect(board.fire('C5')).to eql(Board::WIN) | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment