A.2.1

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5 """
6 self.rolls = []
7 self.current_frame = 1
8 self.frame_rolls = []
9 self.frames = []
10
511 def roll(self, pins):
6 pass
7
12 """
13 Record a roll in the game.
14
15 Args:
16 pins (int): Number of pins knocked down in this roll
17
18 Raises:
19 ValueError: If pins is negative or greater than 10
20 IndexError: If game is already over
21 """
22 # Edge Case: Invalid number of pins
23 if pins < 0:
24 raise ValueError("pins must be between 0 and 10")
25
26 # Edge Case: Too many pins
27 if pins > 10:
28 raise ValueError("pins must be between 0 and 10")
29
30 # Edge Case: Game already finished
31 if len(self.frames) == 10:
32 # Check if we're in fill balls
33 if self.is_tenth_frame_complete():
34 raise IndexError("cannot roll after game is over")
35
36 self.frame_rolls.append(pins)
37 self.rolls.append(pins)
38
39 # Handle frame completion
40 if self.current_frame < 10: # Regular frames (1-9)
41 # Edge Case: Strike in regular frame
42 if len(self.frame_rolls) == 1 and pins == 10:
43 self.frames.append(self.frame_rolls[:])
44 self.frame_rolls = []
45 self.current_frame += 1
46 # Edge Case: Two rolls in regular frame
47 elif len(self.frame_rolls) == 2:
48 # Edge Case: Invalid frame total
49 if sum(self.frame_rolls) > 10 and self.frame_rolls[0] != 10:
50 self.frame_rolls = self.frame_rolls[:1] # Keep first roll
51 raise ValueError("invalid frame pins")
52 self.frames.append(self.frame_rolls[:])
53 self.frame_rolls = []
54 self.current_frame += 1
55 elif self.current_frame == 10: # Tenth frame
56 # Edge Case: Tenth frame validation
57 if len(self.frame_rolls) == 1:
58 # If first roll is a strike, need two more rolls
59 if self.frame_rolls[0] == 10:
60 # Wait for two more rolls
61 pass
62 # Otherwise, wait for second roll
63 else:
64 pass
65 elif len(self.frame_rolls) == 2:
66 # If first roll is a strike, need one more roll
67 if self.frame_rolls[0] == 10:
68 # Wait for one more roll
69 pass
70 # If first two rolls are a spare, need one more roll (fill ball)
71 elif sum(self.frame_rolls[:2]) == 10:
72 # Wait for fill ball
73 pass
74 # If first two rolls are not a strike or spare, frame is complete
75 elif sum(self.frame_rolls[:2]) < 10:
76 self.frames.append(self.frame_rolls[:])
77 self.frame_rolls = []
78 elif len(self.frame_rolls) == 3:
79 # Edge Case: Validate tenth frame fill balls
80 if self.frame_rolls[0] == 10: # First roll was a strike
81 # Second roll can be anything, third roll depends on second
82 if self.frame_rolls[1] != 10 and sum(self.frame_rolls[1:]) > 10:
83 self.frame_rolls = self.frame_rolls[:2] # Remove invalid third roll
84 raise ValueError("Pin count exceeds pins on the lane")
85 elif sum(self.frame_rolls[:2]) == 10: # First two rolls were a spare
86 # Third roll is a fill ball, no validation needed beyond pin count
87 pass
88 else: # Open frame, shouldn't have a third roll
89 self.frame_rolls = self.frame_rolls[:2] # Remove invalid third roll
90 raise ValueError("Pin count exceeds pins on the lane")
91
92 self.frames.append(self.frame_rolls[:])
93 self.frame_rolls = []
94 else: # Tenth frame
95 # Edge Case: Tenth frame validation
96 if len(self.frame_rolls) == 1:
97 # If first roll is a strike, need two more rolls
98 if self.frame_rolls[0] == 10:
99 # Wait for two more rolls
100 pass
101 # Otherwise, wait for second roll
102 else:
103 pass
104 elif len(self.frame_rolls) == 2:
105 # If first roll is a strike, need one more roll
106 if self.frame_rolls[0] == 10:
107 # Wait for one more roll
108 pass
109 # If first two rolls are a spare, need one more roll (fill ball)
110 elif sum(self.frame_rolls[:2]) == 10:
111 # Wait for fill ball
112 pass
113 # If first two rolls are not a strike or spare, frame is complete
114 elif sum(self.frame_rolls[:2]) < 10:
115 self.frames.append(self.frame_rolls[:])
116 self.frame_rolls = []
117 elif len(self.frame_rolls) == 3:
118 # Edge Case: Validate tenth frame fill balls
119 if self.frame_rolls[0] == 10: # First roll was a strike
120 # Second roll can be anything, third roll depends on second
121 if self.frame_rolls[1] != 10 and sum(self.frame_rolls[1:]) > 10:
122 self.frame_rolls = self.frame_rolls[:2] # Remove invalid third roll
123 raise ValueError("Pin count exceeds pins on the lane")
124 elif sum(self.frame_rolls[:2]) == 10: # First two rolls were a spare
125 # Third roll is a fill ball, no validation needed beyond pin count
126 pass
127 else: # Open frame, shouldn't have a third roll
128 self.frame_rolls = self.frame_rolls[:2] # Remove invalid third roll
129 raise ValueError("Pin count exceeds pins on the lane")
130
131 self.frames.append(self.frame_rolls[:])
132 self.frame_rolls = []
133 # Edge Case: Validate tenth frame fill balls
134 if self.frame_rolls[0] == 10: # First roll was a strike
135 # Second roll can be anything, third roll depends on second
136 if self.frame_rolls[1] != 10 and sum(self.frame_rolls[1:]) > 10:
137 self.frame_rolls = self.frame_rolls[:2] # Remove invalid third roll
138 raise ValueError("Pin count exceeds pins on the lane")
139 elif self.frame_rolls[0] != 10 and sum(self.frame_rolls[:2]) == 10: # First two rolls were a spare
140 # Third roll is a fill ball, no validation needed beyond pin count
141 pass
142 else: # Open frame, shouldn't have a third roll
143 self.frame_rolls = self.frame_rolls[:2] # Remove invalid third roll
144 raise ValueError("Pin count exceeds pins on the lane")
145
146 self.frames.append(self.frame_rolls[:])
147 self.frame_rolls = []
148 elif len(self.frame_rolls) == 3:
149 # Edge Case: Validate tenth frame fill balls
150 if self.frame_rolls[0] == 10: # First roll was a strike
151 # Second roll can be anything, third roll depends on second
152 if self.frame_rolls[1] != 10 and sum(self.frame_rolls[1:]) > 10:
153 self.frame_rolls = self.frame_rolls[:2] # Remove invalid third roll
154 raise ValueError("Pin count exceeds pins on the lane")
155 elif self.frame_rolls[0] != 10 and sum(self.frame_rolls[:2]) == 10: # First two rolls were a spare
156 # Third roll is a fill ball, no validation needed beyond pin count
157 pass
158 else: # Open frame, shouldn't have a third roll
159 self.frame_rolls = self.frame_rolls[:2] # Remove invalid third roll
160 raise ValueError("Pin count exceeds pins on the lane")
161
162 self.frames.append(self.frame_rolls[:])
163 self.frame_rolls = []
164
165 def is_tenth_frame_complete(self):
166 """
167 Check if the tenth frame is complete.
168
169 Returns:
170 bool: True if tenth frame is complete, False otherwise
171 """
172 # If we haven't completed 10 frames yet
173 if len(self.frames) < 9: # Less than 9 complete frames
174 return False
175
176 # If we have exactly 9 complete frames, check current frame rolls
177 if len(self.frames) == 9:
178 # We're still rolling in the tenth frame (in self.frame_rolls)
179 # Check if tenth frame is complete based on self.frame_rolls
180 if len(self.frame_rolls) >= 1 and self.frame_rolls[0] == 10: # Strike
181 # Need two more rolls
182 return len(self.frame_rolls) == 3
183 elif len(self.frame_rolls) >= 2 and sum(self.frame_rolls[:2]) == 10: # Spare
184 # Need one more roll
185 return len(self.frame_rolls) == 3
186 elif len(self.frame_rolls) >= 2: # Open frame
187 # Only need two rolls
188 return True
189 else:
190 return False
191
192 # If we have 10 complete frames
193 if len(self.frames) >= 10:
194 tenth_frame = self.frames[9]
195
196 # Edge Case: Tenth frame is a strike
197 if len(tenth_frame) >= 1 and tenth_frame[0] == 10:
198 # Need two more rolls
199 return len(tenth_frame) == 3
200 # Edge Case: Tenth frame is a spare
201 elif len(tenth_frame) >= 2 and sum(tenth_frame[:2]) == 10:
202 # Need one more roll
203 return len(tenth_frame) == 3
204 # Edge Case: Tenth frame is open
205 else:
206 # Only need two rolls
207 return len(tenth_frame) == 2
208
209 return False
210
8211 def score(self):
9 pass
212 """
213 Calculate the total score for the game.
214
215 Returns:
216 int: Total score for the game
217
218 Raises:
219 IndexError: If game is not yet complete
220 """
221 # Edge Case: Game not complete
222 if len(self.frames) < 10 or not self.is_tenth_frame_complete():
223 raise IndexError("game is not yet complete")
224
225 total_score = 0
226 roll_index = 0
227
228 # Edge Case: Score calculation for each frame
229 for frame_num in range(10):
230 frame = self.frames[frame_num]
231
232 if frame[0] == 10: # Strike
233 # Edge Case: Strike scoring
234 total_score += 10
235 # Add next two rolls
236 if frame_num < 9: # Not tenth frame
237 next_frame = self.frames[frame_num + 1]
238 if len(next_frame) >= 1:
239 total_score += next_frame[0]
240 if len(next_frame) >= 2:
241 total_score += next_frame[1]
242 elif frame_num < 8 and len(next_frame) == 1: # If next frame is also a strike
243 next_next_frame = self.frames[frame_num + 2]
244 if len(next_next_frame) >= 1:
245 total_score += next_next_frame[0]
246 else: # Tenth frame
247 if len(frame) >= 2:
248 total_score += frame[1]
249 if len(frame) >= 3:
250 total_score += frame[2]
251 elif sum(frame[:2]) == 10: # Spare
252 # Edge Case: Spare scoring
253 total_score += 10
254 # Add next roll
255 if frame_num < 9: # Not tenth frame
256 next_frame = self.frames[frame_num + 1]
257 if len(next_frame) >= 1:
258 total_score += next_frame[0]
259 else: # Tenth frame
260 if len(frame) >= 3:
261 total_score += frame[2]
262 else: # Open frame
263 # Edge Case: Open frame scoring
264 total_score += sum(frame[:2])
265
266 return total_score
267
268 # Handled Edge Cases: Invalid number of pins, too many pins, game already finished, strike in regular frame, two rolls in regular frame, invalid frame total, tenth frame validation, invalid tenth frame fill balls, tenth frame is a strike, tenth frame is a spare, tenth frame is open, game not complete, strike scoring, spare scoring, open frame scoring
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.