Skip to content

Instantly share code, notes, and snippets.

@elliotchance
Created May 3, 2016 11:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elliotchance/0fb3374118253b494fbc91cc1991d5ed to your computer and use it in GitHub Desktop.
Save elliotchance/0fb3374118253b494fbc91cc1991d5ed to your computer and use it in GitHub Desktop.
Kata: The Bowling Game
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
@AKinbusola
Copy link

How do I run this game

@elliotchance
Copy link
Author

@AKinbusola you should be able to run this with pytest:

pytest bowling.py

If pytest it's not installed:

pip install pytest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment