Skip to content

Instantly share code, notes, and snippets.

@jkarmel
Created February 27, 2015 22:20
Show Gist options
  • Save jkarmel/bba95aecf9c0b9b0f743 to your computer and use it in GitHub Desktop.
Save jkarmel/bba95aecf9c0b9b0f743 to your computer and use it in GitHub Desktop.
# #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
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