Merry

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5 """
6 self.rolls = []
7
58 def roll(self, pins):
6 pass
7
9 """
10 Record a roll in the game.
11
12 Args:
13 pins (int): Number of pins knocked down in this roll.
14
15 Raises:
16 ValueError: If the number of pins is invalid.
17 IndexError: If the game has already ended.
18 """
19 # Validate pin count
20 if pins < 0 or pins > 10:
21 raise ValueError("Pins must be between 0 and 10")
22
23 # Check if game has already ended
24 if self.is_game_complete():
25 raise IndexError("Game has already ended")
26
27 # Validate frame pin limits
28 if len(self.rolls) > 0:
29 frame_index = self.get_current_frame_index()
30
31 if frame_index < 9: # Regular frames (0-8)
32 # If this is the second roll of a frame
33 if len(self.rolls) % 2 == 1 and not self.is_strike(len(self.rolls) - 1):
34 if self.rolls[-1] + pins > 10:
35 raise ValueError("Cannot knock down more pins than available")
36 else: # Tenth frame
37 self.validate_tenth_frame_roll(pins)
38
39 self.rolls.append(pins)
40
41 def validate_tenth_frame_roll(self, pins):
42 """
43 Validate a roll in the tenth frame.
44
45 Args:
46 pins (int): Number of pins knocked down.
47
48 Raises:
49 ValueError: If the roll is invalid for the tenth frame.
50 """
51 # Get the start index of the tenth frame
52 roll_index = self.get_tenth_frame_start_index()
53 tenth_frame_rolls = len(self.rolls) - roll_index
54
55 if tenth_frame_rolls == 1: # Second roll of tenth frame
56 first_roll = self.rolls[roll_index]
57 if first_roll == 10: # First roll was a strike
58 # Second roll can be anything 0-10
59 pass
60 elif first_roll + pins > 10: # First roll wasn't a strike
61 raise ValueError("Cannot knock down more pins than available")
62 elif tenth_frame_rolls == 2: # Third roll of tenth frame
63 first_roll = self.rolls[roll_index]
64 second_roll = self.rolls[roll_index + 1]
65
66 if first_roll == 10: # First roll was a strike
67 if second_roll == 10: # Second roll was also a strike
68 # Third roll can be anything 0-10
69 pass
70 else: # Second roll wasn't a strike
71 if second_roll + pins > 10:
72 raise ValueError("Cannot knock down more pins than available")
73 elif first_roll + second_roll == 10: # First two rolls were a spare
74 # Third roll can be anything 0-10
75 pass
76 # If first two rolls were an open frame, there shouldn't be a third roll
77 # This case should be caught by is_game_complete()
78
79 def get_current_frame_index(self):
80 """
81 Get the current frame index (0-9).
82
83 Returns:
84 int: Current frame index.
85 """
86 roll_index = 0
87 frame_index = 0
88
89 while frame_index < 9 and roll_index < len(self.rolls):
90 if self.is_strike(roll_index):
91 roll_index += 1
92 else:
93 roll_index += 2
94 frame_index += 1
95
96 return frame_index
97
98 def get_tenth_frame_start_index(self):
99 """
100 Get the start index of the tenth frame.
101
102 Returns:
103 int: Start index of the tenth frame.
104 """
105 roll_index = 0
106 frame_index = 0
107
108 while frame_index < 9 and roll_index < len(self.rolls):
109 if self.is_strike(roll_index):
110 roll_index += 1
111 else:
112 roll_index += 2
113 frame_index += 1
114
115 return roll_index
116
8117 def score(self):
9 pass
118 """
119 Calculate the total score for the game.
120
121 Returns:
122 int: The total score of the game.
123
124 Raises:
125 IndexError: If the game is not yet complete.
126 """
127 # Edge Case: Game not yet complete
128 if not self.is_game_complete():
129 raise IndexError("Game is not yet complete")
130
131 score = 0
132 roll_index = 0
133
134 for frame in range(10):
135 # Edge Case: Strike in frames 0-8
136 if self.is_strike(roll_index) and frame < 9:
137 score += 10 + self.strike_bonus(roll_index)
138 roll_index += 1
139 # Edge Case: Spare in frames 0-8
140 elif self.is_spare(roll_index) and frame < 9:
141 score += 10 + self.spare_bonus(roll_index)
142 roll_index += 2
143 # Edge Case: Open frame in frames 0-8
144 elif frame < 9:
145 score += self.sum_of_balls_in_frame(roll_index)
146 roll_index += 2
147 # Edge Case: Tenth frame handling
148 else: # frame == 9 (10th frame)
149 # For the 10th frame, we sum all the rolls in that frame
150 # This includes any bonus rolls
151 tenth_frame_rolls = 0
152 remaining_rolls = len(self.rolls) - roll_index
153
154 # Sum all remaining rolls (which are the 10th frame rolls including bonuses)
155 for i in range(remaining_rolls):
156 tenth_frame_rolls += self.rolls[roll_index + i]
157
158 score += tenth_frame_rolls
159 roll_index += remaining_rolls
160
161 return score
162
163 def is_game_complete(self):
164 """
165 Check if the game is complete (10 frames have been played).
166
167 Returns:
168 bool: True if the game is complete, False otherwise.
169 """
170 # Need at least 12 rolls for a game with all strikes except last frame
171 # and at least 20 rolls for a game with no strikes or spares
172 if len(self.rolls) < 12:
173 return False
174
175 # Count frames to determine if we've completed 10 frames
176 roll_index = 0
177 frames_completed = 0
178
179 # Process first 9 frames
180 while frames_completed < 9 and roll_index < len(self.rolls):
181 if roll_index < len(self.rolls) and self.is_strike(roll_index):
182 roll_index += 1
183 else:
184 if roll_index + 1 < len(self.rolls):
185 roll_index += 2
186 else:
187 # Not enough rolls to complete this frame
188 return False
189 frames_completed += 1
190
191 # If we haven't completed 9 frames, game is not complete
192 if frames_completed < 9:
193 return False
194
195 # Process 10th frame
196 if roll_index >= len(self.rolls):
197 return False
198
199 # Check 10th frame scenarios
200 if roll_index < len(self.rolls) and self.is_strike(roll_index): # First roll is strike
201 # Need two more rolls for strike in 10th frame
202 return roll_index + 2 < len(self.rolls)
203 else:
204 if roll_index + 1 >= len(self.rolls):
205 return False
206 frame_score = self.rolls[roll_index] + self.rolls[roll_index + 1]
207 if frame_score == 10: # Spare in 10th frame
208 # Need one more roll for spare in 10th frame
209 return roll_index + 2 < len(self.rolls)
210 else: # Open frame
211 return roll_index + 1 < len(self.rolls)
212
213 def is_strike(self, roll_index):
214 """
215 Check if a roll is a strike.
216
217 Args:
218 roll_index (int): Index of the roll to check.
219
220 Returns:
221 bool: True if the roll is a strike, False otherwise.
222 """
223 return self.rolls[roll_index] == 10
224
225 def is_spare(self, roll_index):
226 """
227 Check if a frame is a spare.
228
229 Args:
230 roll_index (int): Index of the first roll in the frame.
231
232 Returns:
233 bool: True if the frame is a spare, False otherwise.
234 """
235 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
236
237 def strike_bonus(self, roll_index):
238 """
239 Calculate the bonus for a strike.
240
241 Args:
242 roll_index (int): Index of the strike roll.
243
244 Returns:
245 int: The bonus points for the strike.
246 """
247 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
248
249 def spare_bonus(self, roll_index):
250 """
251 Calculate the bonus for a spare.
252
253 Args:
254 roll_index (int): Index of the first roll in the spare frame.
255
256 Returns:
257 int: The bonus points for the spare.
258 """
259 return self.rolls[roll_index + 2]
260
261 def sum_of_balls_in_frame(self, roll_index):
262 """
263 Calculate the sum of pins knocked down in a frame.
264
265 Args:
266 roll_index (int): Index of the first roll in the frame.
267
268 Returns:
269 int: The sum of pins knocked down in the frame.
270 """
271 return self.rolls[roll_index] + self.rolls[roll_index + 1]
272
273 def is_tenth_frame(self):
274 """
275 Check if we are currently in the tenth frame.
276
277 Returns:
278 bool: True if we are in the tenth frame, False otherwise.
279 """
280 # Count completed frames
281 roll_index = 0
282 frames_completed = 0
283
284 while frames_completed < 9 and roll_index < len(self.rolls):
285 if roll_index < len(self.rolls) and self.is_strike(roll_index):
286 roll_index += 1
287 else:
288 if roll_index + 1 < len(self.rolls):
289 roll_index += 2
290 else:
291 break # Not enough rolls to complete this frame
292 frames_completed += 1
293
294 # If we've completed 9 frames, we're in the 10th frame
295 return frames_completed == 9 and roll_index < len(self.rolls)
296
297 # Handled Edge Cases: Invalid pin count, game already completed, game not yet complete, strike in frames 0-8, spare in frames 0-8, open frame in frames 0-8, tenth frame handling
Test NameStatus
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_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.