Bowling: missing test
gemp opened this issue · 1 comments
gemp commented
I stumbled upon a case that wasn't covered by the bowling test suite. This solution adds the next frame bonus to a spare followed by a strike, i.e: [5,5,10,+6][10,6][6,0]...
All current tests pass with this bug.
Proposed test:
def test_a_spare_followed_by_a_strike_should_not_get_bonus_from_next_frame
game = Game.new
rolls = [5, 5, 10, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
rolls.each { |pins| game.roll(pins) }
assert_equal 42, game.score
end
kotp commented
class Game
def initialize
@frames = Hash.new { |frame, key| frame[key] = Frame.new(key) }
@frames[1]
end
def roll(pins)
game_should_not_be_over
next_frame if current_frame.complete?
current_frame.add(pins)
add_bonus if current_frame.complete?
end
def score
game_must_be_over
@frames.values.map(&:score).sum
end
private
def current
@frames.keys.max
end
def current_frame
@frames[current]
end
def next_frame
@frames[current.next]
end
def previous(prev = 1)
current >= prev ? @frames[current - prev] : Frame.new(0)
end
def add_bonus
previous.add_bonus(current_frame.points(2))
previous(2).add_bonus(current_frame.points(1), previous.strike?)
end
def game_over
current_frame.last? && current_frame.complete?
end
def game_must_be_over
raise BowlingError, "The game is not over" unless game_over
end
def game_should_not_be_over
raise BowlingError, "The game is over" if game_over
end
class Frame
def initialize(turn)
@points = []
@complete = false
@last = turn == 10
end
def add(pins)
validate(pins)
@points << pins
@complete = @points.size == rolls_to_complete
end
def points(max = 3)
@points.first(max)
end
def score
@points.sum
end
def add_bonus(rolls, should_add = true)
return unless bonus? && should_add
bonus = strike? ? rolls.sum : rolls.first
@points << bonus
end
def bonus?
spare? || strike?
end
def strike?
@points.first == 10
end
def spare?
!strike? && score >= 10
end
def complete?
@complete
end
def last?
@last
end
private
def rolls_to_complete
if last? then bonus? ? 3 : 2 else strike? ? 1 : 2 end
end
def validate(pins)
incoherent_pins = !pins.between?(0,10)
invalid = last? ? score < 20 && score + pins > 20 : score + pins > 10
raise BowlingError, "Wrong number of pins" if incoherent_pins || invalid
end
end
class BowlingError < StandardError
end
end
Posted here for "we are talking about it here" convenience.