Created
May 3, 2016 11:39
-
-
Save elliotchance/0fb3374118253b494fbc91cc1991d5ed to your computer and use it in GitHub Desktop.
Kata: The Bowling Game
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
import pytest | |
class Game: | |
def __init__(self): | |
self.frames = [[]] | |
self.allow_bonus_roll = False | |
@property | |
def last_frame(self): | |
return self.frames[-1] | |
@property | |
def total_frames(self): | |
return len(self.frames) | |
def validate_roll(self, pins): | |
if pins < 0: | |
raise ValueError('Cannot roll less than zero pins.') | |
if pins > 10: | |
raise ValueError('Cannot roll more than ten pins.') | |
if sum(self.last_frame) > 10: | |
raise ValueError('You can only clear 10 pins in a single frame.') | |
if self.total_frames > 10 and sum(self.frames[9]) < 10: | |
raise ValueError('Cannot bowl more than 10 frames.') | |
def roll(self, pins): | |
self.last_frame.append(pins) | |
self.validate_roll(pins) | |
if len(self.last_frame) == 2 or pins == 10: | |
self.frames.append([]) | |
def frame_is_a_strike(self, frame): | |
return frame == [10] | |
def score_for_frame_with_bonuses(self, i): | |
total = sum(self.frames[i]) | |
try: | |
if self.frame_is_a_strike(self.frames[i]): | |
total += self.frames[i + 1][0] | |
if len(self.frames[i + 1]) == 2: | |
total += self.frames[i + 1][1] | |
else: | |
total += self.frames[i + 2][0] | |
elif total == 10: | |
total += self.frames[i + 1][0] | |
except IndexError: | |
pass | |
return total | |
def score(self): | |
total = 0 | |
for i in xrange(self.total_frames): | |
total += self.score_for_frame_with_bonuses(i) | |
return total | |
def test_the_score_starts_at_zero(): | |
game = Game() | |
assert game.score() == 0 | |
def test_rolling_less_than_zero_is_not_allowed(): | |
game = Game() | |
with pytest.raises(ValueError) as e: | |
game.roll(-1) | |
assert e.value.message == 'Cannot roll less than zero pins.' | |
def test_rolling_zero_is_allowed(): | |
game = Game() | |
game.roll(0) | |
assert game.score() == 0 | |
def test_rolling_greater_than_ten_is_not_allowed(): | |
game = Game() | |
with pytest.raises(ValueError) as e: | |
game.roll(11) | |
assert e.value.message == 'Cannot roll more than ten pins.' | |
def test_a_single_roll(): | |
game = Game() | |
game.roll(5) | |
assert game.score() == 5 | |
def test_two_rolls_without_spare(): | |
game = Game() | |
game.roll(5) | |
game.roll(3) | |
assert game.score() == 8 | |
def test_more_than_ten_pins_in_a_frame(): | |
game = Game() | |
game.roll(5) | |
with pytest.raises(ValueError) as e: | |
game.roll(6) | |
assert e.value.message == 'You can only clear 10 pins in a single frame.' | |
def test_three_rolls(): | |
game = Game() | |
game.roll(5) | |
game.roll(3) | |
game.roll(7) | |
assert game.score() == 15 | |
def test_spare_includes_bonus_roll(): | |
game = Game() | |
game.roll(5) | |
game.roll(5) | |
game.roll(7) | |
assert game.score() == 24 | |
def test_spare_may_have_no_bonus(): | |
game = Game() | |
game.roll(5) | |
game.roll(5) | |
game.roll(0) | |
assert game.score() == 10 | |
def test_strike_includes_first_bonus_roll(): | |
game = Game() | |
game.roll(10) | |
game.roll(7) | |
assert game.score() == 24 | |
def test_spare_missing_next_roll(): | |
game = Game() | |
game.roll(5) | |
game.roll(5) | |
assert game.score() == 10 | |
def test_strike_includes_second_bonus_roll(): | |
game = Game() | |
game.roll(10) | |
game.roll(7) | |
game.roll(2) | |
assert game.score() == 28 | |
def test_strike_could_be_followed_by_another_strike(): | |
game = Game() | |
game.roll(10) | |
game.roll(10) | |
game.roll(2) | |
assert game.score() == 36 | |
def test_too_many_rolls(): | |
game = Game() | |
for roll in xrange(20): | |
game.roll(3) | |
with pytest.raises(ValueError) as e: | |
game.roll(5) | |
assert e.value.message == 'Cannot bowl more than 10 frames.' | |
def test_last_frame_spare_allows_bonus_roll(): | |
game = Game() | |
for i in xrange(18): | |
game.roll(1) | |
game.roll(8) | |
game.roll(2) | |
game.roll(3) | |
assert game.score() == 34 | |
def test_perfect_game(): | |
game = Game() | |
for i in xrange(11): | |
game.roll(10) | |
assert game.score() == 300 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@AKinbusola you should be able to run this with
pytest
:If pytest it's not installed: