great

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5 """
6 self.rolls = []
7 self.current_roll = 0
8
59 def roll(self, pins):
6 pass
7
10 """
11 Record a roll in the game.
12
13 Args:
14 pins (int): Number of pins knocked down in this roll.
15
16 Raises:
17 ValueError: If the number of pins is invalid.
18 """
19 # Edge Case: Invalid pin count (negative or more than 10)
20 if pins < 0 or pins > 10:
21 raise ValueError("Pins must be between 0 and 10")
22
23 # Edge Case: Game already completed (10 frames plus any fill balls)
24 if self.is_game_complete():
25 raise ValueError("Game is already finished")
26
27 # Edge Case: Validate frame pin limits
28 if not self.is_valid_roll(pins):
29 raise ValueError("Cannot knock down more pins than remaining")
30
31 self.rolls.append(pins)
32 self.current_roll += 1
33
34 def is_valid_roll(self, pins):
35 """
36 Check if the roll is valid given the current game state.
37
38 Args:
39 pins (int): Number of pins knocked down in this roll.
40
41 Returns:
42 bool: True if the roll is valid, False otherwise.
43 """
44 if len(self.rolls) == 0:
45 return True
46
47 # If this would be the first roll of a frame, it's always valid
48 if len(self.rolls) % 2 == 0 and not (len(self.rolls) > 0 and self.rolls[-1] == 10):
49 return True
50
51 # If the previous roll was a strike, we're starting a new frame
52 if len(self.rolls) > 0 and self.rolls[-1] == 10:
53 return True
54
55 # We're on the second roll of a frame, check if pins don't exceed 10
56 if len(self.rolls) > 0:
57 return self.rolls[-1] + pins <= 10
58
59 return True
60
861 def score(self):
9 pass
62 """
63 Calculate the total score for the game.
64
65 Returns:
66 int: The total score of the game.
67
68 Raises:
69 ValueError: If the game is not yet complete.
70 """
71 # Edge Case: Game not yet complete
72 if not self.is_game_complete():
73 raise ValueError("Game is not finished yet")
74
75 score = 0
76 roll_index = 0
77
78 for frame in range(10):
79 # Edge Case: Strike in regular frames (1-9)
80 if self.is_strike(roll_index) and frame < 9:
81 score += 10 + self.strike_bonus(roll_index)
82 roll_index += 1
83 # Edge Case: Spare in regular frames (1-9)
84 elif self.is_spare(roll_index) and frame < 9:
85 score += 10 + self.spare_bonus(roll_index)
86 roll_index += 2
87 # Edge Case: Open frame in regular frames (1-9)
88 elif frame < 9:
89 score += self.sum_of_balls_in_frame(roll_index)
90 roll_index += 2
91 # Edge Case: Tenth frame handling
92 else: # frame == 9 (10th frame)
93 # Edge Case: Strike in 10th frame
94 if self.is_strike(roll_index):
95 # Edge Case: Second strike in 10th frame
96 if self.is_strike(roll_index + 1):
97 # Edge Case: Third strike in 10th frame
98 score += 10 + 10 + self.rolls[roll_index + 2]
99 else:
100 score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
101 roll_index += 2 # Strike in 10th takes only one roll initially
102 # Edge Case: Spare in 10th frame
103 elif self.is_spare(roll_index):
104 score += 10 + self.rolls[roll_index + 2]
105 roll_index += 2
106 # Edge Case: Open frame in 10th frame
107 else:
108 score += self.sum_of_balls_in_frame(roll_index)
109 roll_index += 2
110
111 return score
112
113 def is_game_complete(self):
114 """
115 Check if the game is complete (10 frames have been played, including fill balls if needed).
116
117 Returns:
118 bool: True if the game is complete, False otherwise.
119 """
120 roll_index = 0
121 frame_count = 0
122
123 # Edge Case: Count frames properly including strikes and spares in first 9 frames
124 while frame_count < 9 and roll_index < len(self.rolls):
125 if self.is_strike(roll_index):
126 roll_index += 1
127 else:
128 roll_index += 2
129 frame_count += 1
130
131 # Edge Case: Handle 10th frame properly with fill balls
132 if frame_count == 9 and roll_index < len(self.rolls):
133 # If 10th frame was a strike
134 if self.is_strike(roll_index):
135 # Need two more rolls
136 return roll_index + 2 < len(self.rolls)
137 # If 10th frame was a spare
138 elif roll_index + 1 < len(self.rolls) and self.is_spare(roll_index):
139 # Need one more roll
140 return roll_index + 2 < len(self.rolls)
141 # If 10th frame was open
142 else:
143 # Need two rolls total for this frame
144 return roll_index + 1 < len(self.rolls)
145
146 return False
147
148 def is_strike(self, roll_index):
149 """
150 Check if a roll is a strike.
151
152 Args:
153 roll_index (int): Index of the roll to check.
154
155 Returns:
156 bool: True if the roll is a strike, False otherwise.
157 """
158 # Edge Case: Index out of bounds
159 if roll_index >= len(self.rolls):
160 return False
161 return self.rolls[roll_index] == 10
162
163 def is_spare(self, roll_index):
164 """
165 Check if two rolls form a spare.
166
167 Args:
168 roll_index (int): Index of the first roll to check.
169
170 Returns:
171 bool: True if the two rolls form a spare, False otherwise.
172 """
173 # Edge Case: Index out of bounds for either roll
174 if roll_index + 1 >= len(self.rolls):
175 return False
176 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
177
178 def strike_bonus(self, roll_index):
179 """
180 Calculate the bonus for a strike.
181
182 Args:
183 roll_index (int): Index of the strike roll.
184
185 Returns:
186 int: The bonus points for the strike.
187 """
188 # Edge Case: Not enough rolls for bonus calculation
189 if roll_index + 2 >= len(self.rolls):
190 return 0
191 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
192
193 def spare_bonus(self, roll_index):
194 """
195 Calculate the bonus for a spare.
196
197 Args:
198 roll_index (int): Index of the first roll in the spare frame.
199
200 Returns:
201 int: The bonus points for the spare.
202 """
203 # Edge Case: Not enough rolls for bonus calculation
204 if roll_index + 2 >= len(self.rolls):
205 return 0
206 return self.rolls[roll_index + 2]
207
208 def sum_of_balls_in_frame(self, roll_index):
209 """
210 Calculate the sum of pins knocked down in a frame.
211
212 Args:
213 roll_index (int): Index of the first roll in the frame.
214
215 Returns:
216 int: The sum of pins knocked down in the frame.
217 """
218 # Edge Case: Not enough rolls in frame
219 if roll_index + 1 >= len(self.rolls):
220 return 0 if roll_index >= len(self.rolls) else self.rolls[roll_index]
221 return self.rolls[roll_index] + self.rolls[roll_index + 1]
222
223 # Handled Edge Cases: Invalid pin count, game already completed, game not yet complete,
224 # strike in regular frames, spare in regular frames, open frame in regular frames,
225 # strike in 10th frame, second strike in 10th frame, third strike in 10th frame,
226 # spare in 10th frame, open frame in 10th frame, index out of bounds,
227 # not enough rolls for bonus calculation, not enough rolls in frame
Test NameStatus
test_the_second_bonus_rolls_after_a_strike_in_the_last_frame_cannot_be_a_strike_if_the_first_one_is_not_a_strike
Fail
test_two_bonus_rolls_after_a_strike_in_the_last_frame_cannot_score_more_than_10_points
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_an_unstarted_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_two_bonus_rolls_after_a_strike_in_the_last_frame_can_score_more_than_10_points_if_one_is_a_strike
Pass
test_two_rolls_in_a_frame_cannot_score_more_than_10_points
Pass

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