Finished
11class BowlingGame:
22 def __init__(self):
3 pass
3 self.rolls = []
4 self.current_frame = 1
5 self.frame_rolls = 0 # Rolls in the current frame
6 self.game_over = False
47
58 def roll(self, pins):
6 pass
9 # Edge Case: Game is already over
10 if self.game_over:
11 raise ValueError("Cannot roll after game is over")
12
13 # Edge Case: Invalid pin count (negative)
14 if pins < 0:
15 raise ValueError("Negative roll is invalid")
16
17 # Edge Case: Invalid pin count (more than 10)
18 if pins > 10:
19 raise ValueError("Pin count exceeds pins on the lane")
20
21 # Edge Case: First roll in frame exceeds 10 pins
22 if self.frame_rolls == 0 and pins == 10:
23 # Strike - special handling for frame 10
24 if self.current_frame == 10:
25 self.rolls.append(pins)
26 self.frame_rolls += 1
27 if self.frame_rolls == 3:
28 self.game_over = True
29 return
30 else:
31 self.rolls.append(pins)
32 self.current_frame += 1
33 self.frame_rolls = 0
34 return
35
36 # Edge Case: Second roll exceeds remaining pins
37 if self.frame_rolls == 1:
38 # For frame 10, we need special handling
39 if self.current_frame == 10:
40 # If it's the second roll in frame 10
41 if len(self.rolls) == 1 and self.rolls[0] == 10: # First roll was strike
42 if pins > 10:
43 raise ValueError("Pin count exceeds pins on the lane")
44 elif len(self.rolls) > 0 and self.rolls[-1] == 10: # First roll of frame 10 was strike
45 if pins > 10:
46 raise ValueError("Pin count exceeds pins on the lane")
47 else: # Normal second roll in frame 10
48 if len(self.rolls) > 0 and self.rolls[-1] + pins > 10:
49 raise ValueError("Pin count exceeds pins on the lane")
50 else:
51 # Normal frame - check if sum exceeds 10
52 if self.rolls[-1] + pins > 10:
53 raise ValueError("Pin count exceeds pins on the lane")
54 # Edge Case: Third roll in frame 10 (fill ball after strike)
55 if self.current_frame == 10 and self.frame_rolls == 2:
56 # If first roll was strike, check fill ball constraints
57 if len(self.rolls) >= 2 and self.rolls[-2] == 10:
58 # If second roll was also a strike, third roll can be up to 10
59 if self.rolls[-1] == 10:
60 if pins > 10:
61 raise ValueError("Pin count exceeds pins on the lane")
62 else:
63 # Second roll was not a strike, so third roll + second roll cannot exceed 10
64 if self.rolls[-1] + pins > 10:
65 raise ValueError("Pin count exceeds pins on the lane")
66 # If first two rolls were a spare, third roll can be up to 10
67 elif len(self.rolls) >= 2 and self.rolls[-2] + self.rolls[-1] == 10:
68 if pins > 10:
69 raise ValueError("Pin count exceeds pins on the lane")
70
71 self.rolls.append(pins)
72 self.frame_rolls += 1
73
74 # Handle frame completion
75 if self.current_frame == 10:
76 # Special handling for frame 10
77 if self.frame_rolls == 2:
78 # If it's not a spare or strike, game is over
79 if self.rolls[-2] + self.rolls[-1] < 10:
80 self.game_over = True
81 elif self.frame_rolls == 3:
82 self.game_over = True
83 else:
84 if self.frame_rolls == 2:
85 self.current_frame += 1
86 self.frame_rolls = 0
787
888 def score(self):
9 pass
89 # Edge Case: Game is not yet over
90 if len(self.rolls) > 0 and not self.game_over and not (self.current_frame > 10 or
91 (self.current_frame == 10 and
92 ((self.frame_rolls == 2 and sum(self.rolls[-2:]) < 10) or
93 (self.frame_rolls == 3)))):
94 raise ValueError("Score cannot be taken until the end of the game")
95
96 # Handle case with no rolls
97 if len(self.rolls) == 0:
98 return 0
99
100 total_score = 0
101 roll_index = 0
102
103 for frame in range(10):
104 # Edge Case: Not enough rolls to calculate score
105 if roll_index >= len(self.rolls):
106 raise ValueError("Not enough rolls to calculate score")
107
108 if frame < 9: # Frames 1-9
109 # Check for strike
110 if self.rolls[roll_index] == 10:
111 # Strike - add 10 plus next two rolls
112 # Edge Case: Not enough rolls for strike bonus
113 if roll_index + 2 >= len(self.rolls):
114 raise ValueError("Not enough rolls to calculate score")
115 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
116 roll_index += 1
117 else:
118 # Check for spare
119 frame_score = self.rolls[roll_index] + self.rolls[roll_index + 1]
120 # Edge Case: Frame score exceeds 10 (invalid)
121 if frame_score > 10:
122 raise ValueError("Invalid frame score")
123 if frame_score == 10:
124 # Spare - add 10 plus next roll
125 # Edge Case: Not enough rolls for spare bonus
126 if roll_index + 2 >= len(self.rolls):
127 raise ValueError("Not enough rolls to calculate score")
128 total_score += 10 + self.rolls[roll_index + 2]
129 else:
130 # Open frame
131 total_score += frame_score
132 roll_index += 2
133 else: # 10th frame
134 # Edge Case: Not enough rolls in 10th frame
135 if len(self.rolls) - roll_index < 2:
136 raise ValueError("Not enough rolls to calculate score")
137
138 # Calculate 10th frame score
139 if self.rolls[roll_index] == 10: # First roll is strike
140 if len(self.rolls) - roll_index < 3:
141 raise ValueError("Not enough rolls to calculate score")
142 total_score += self.rolls[roll_index] + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
143 elif self.rolls[roll_index] + self.rolls[roll_index + 1] == 10: # Spare
144 if len(self.rolls) - roll_index < 3:
145 raise ValueError("Not enough rolls to calculate score")
146 total_score += self.rolls[roll_index] + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
147 else: # Open frame
148 total_score += self.rolls[roll_index] + self.rolls[roll_index + 1]
149
150 return total_score
151
152 # Handled Edge Cases: Game already over, negative pins, pins > 10, strike in frame 10, invalid pin count in frame, game not over when scoring, not enough rolls for strike/spare bonus, invalid frame score, not enough rolls in 10th frame
Test NameStatus
test_an_unstarted_game_cannot_be_scored
Fail
test_a_roll_cannot_score_more_than_10_points
Pass
test_a_spare_followed_by_zeros_is_worth_ten_points
Pass
test_a_spare_in_the_last_frame_gets_a_one_roll_bonus_that_is_counted_once
Pass
test_a_strike_earns_ten_points_in_a_frame_with_a_single_roll
Pass
test_a_strike_in_the_last_frame_gets_a_two_roll_bonus_that_is_counted_once
Pass
test_a_strike_with_the_one_roll_bonus_after_a_spare_in_the_last_frame_does_not_get_a_bonus
Pass
test_all_strikes_is_a_perfect_game
Pass
test_an_incomplete_game_cannot_be_scored
Pass
test_bonus_roll_after_a_strike_in_the_last_frame_cannot_score_more_than_10_points
Pass
test_bonus_roll_for_a_spare_in_the_last_frame_must_be_rolled_before_score_can_be_calculated
Pass
test_bonus_rolls_for_a_strike_in_the_last_frame_must_be_rolled_before_score_can_be_calculated
Pass
test_both_bonus_rolls_for_a_strike_in_the_last_frame_must_be_rolled_before_score_can_be_calculated
Pass
test_cannot_roll_after_bonus_roll_for_spare
Pass
test_cannot_roll_after_bonus_rolls_for_strike
Pass
test_cannot_roll_if_game_already_has_ten_frames
Pass
test_consecutive_spares_each_get_a_one_roll_bonus
Pass
test_consecutive_strikes_each_get_the_two_roll_bonus
Pass
test_last_two_strikes_followed_by_only_last_bonus_with_non_strike_points
Pass
test_points_scored_in_the_roll_after_a_spare_are_counted_twice
Pass
test_points_scored_in_the_two_rolls_after_a_strike_are_counted_twice_as_a_bonus
Pass
test_rolling_a_spare_with_the_two_roll_bonus_does_not_get_a_bonus_roll
Pass
test_rolls_cannot_score_negative_points
Pass
test_second_bonus_roll_after_a_strike_in_the_last_frame_cannot_score_more_than_10_points
Pass
test_should_be_able_to_score_a_game_with_all_zeros
Pass
test_should_be_able_to_score_a_game_with_no_strikes_or_spares
Pass
test_strikes_with_the_two_roll_bonus_do_not_get_bonus_rolls
Pass
test_the_second_bonus_rolls_after_a_strike_in_the_last_frame_cannot_be_a_strike_if_the_first_one_is_not_a_strike
Pass
test_two_bonus_rolls_after_a_strike_in_the_last_frame_can_score_more_than_10_points_if_one_is_a_strike
Pass
test_two_bonus_rolls_after_a_strike_in_the_last_frame_cannot_score_more_than_10_points
Pass
test_two_rolls_in_a_frame_cannot_score_more_than_10_points
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.