agent

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 is already over
24 if self.is_game_over():
25 raise ValueError("Game has ended")
26
27 # Edge Case: In a frame, the sum of pins cannot exceed 10 (except for strikes)
28 # Check if this is the second roll in a frame (not a strike)
29 if (self.current_roll > 0 and
30 (self.current_roll + 1) % 2 == 0 and
31 self.rolls[-1] != 10): # Previous roll wasn't a strike
32 if self.rolls[-1] + pins > 10:
33 raise ValueError("Cannot knock down more than 10 pins in a frame")
34
35 self.rolls.append(pins)
36 self.current_roll += 1
37
838 def score(self):
9 pass
39 """
40 Calculate the total score for the game.
41
42 Returns:
43 int: The total score of the game.
44
45 Raises:
46 ValueError: If the game is not yet complete.
47 """
48 # Edge Case: Game is not complete
49 if not self.is_game_over():
50 raise ValueError("Game is not complete yet")
51
52 total_score = 0
53 roll_index = 0
54
55 for frame in range(10):
56 # Edge Case: Strike (10 pins in first roll)
57 if self.is_strike(roll_index):
58 total_score += 10 + self.strike_bonus(roll_index)
59 roll_index += 1
60
61 # Edge Case: Tenth frame strike with insufficient rolls
62 if frame == 9 and roll_index > len(self.rolls):
63 raise ValueError("Not enough rolls to calculate score")
64 # Edge Case: Spare (10 pins in two rolls)
65 elif self.is_spare(roll_index):
66 total_score += 10 + self.spare_bonus(roll_index)
67 roll_index += 2
68
69 # Edge Case: Tenth frame spare with insufficient rolls
70 if frame == 9 and roll_index > len(self.rolls):
71 raise ValueError("Not enough rolls to calculate score")
72 # Edge Case: Open frame (less than 10 pins in two rolls)
73 else:
74 total_score += self.sum_of_balls_in_frame(roll_index)
75 roll_index += 2
76
77 return total_score
78
79 def is_game_over(self):
80 """
81 Check if the game is over.
82
83 Returns:
84 bool: True if the game is over, False otherwise.
85 """
86 # Special case: if we have exactly 20 rolls, check if it's a complete game with all open frames
87 if len(self.rolls) == 20:
88 # Count frames - if we have exactly 10 frames with 2 rolls each, game is over
89 frame_count = 0
90 roll_index = 0
91
92 while roll_index < len(self.rolls) and frame_count < 10:
93 if self.rolls[roll_index] == 10: # Strike
94 frame_count += 1
95 roll_index += 1
96 else:
97 # Need two rolls for a non-strike frame
98 if roll_index + 1 < len(self.rolls):
99 frame_count += 1
100 roll_index += 2
101 else:
102 # Incomplete frame
103 break
104
105 # If we completed exactly 10 frames with 20 rolls, game is over
106 if frame_count == 10 and roll_index == 20:
107 return True
108
109 # Count frames properly considering strikes
110 frame_count = 0
111 roll_index = 0
112
113 # Count frames until we reach 10 or run out of rolls
114 while roll_index < len(self.rolls) and frame_count < 10:
115 if self.rolls[roll_index] == 10: # Strike
116 frame_count += 1
117 roll_index += 1
118 else:
119 # Need two rolls for a non-strike frame
120 if roll_index + 1 < len(self.rolls):
121 frame_count += 1
122 roll_index += 2
123 else:
124 # Incomplete frame
125 return False
126
127 # If we haven't completed 10 frames, game is not over
128 if frame_count < 10:
129 return False
130
131 # We've completed 10 frames, now check if we need fill balls
132 # Find the start of the 10th frame
133 tenth_frame_start = self._find_tenth_frame_start()
134 if tenth_frame_start is None:
135 return False
136
137 tenth_frame_first = self.rolls[tenth_frame_start]
138
139 # If 10th frame is a strike
140 if tenth_frame_first == 10:
141 # Need exactly 2 fill balls
142 return len(self.rolls) == tenth_frame_start + 3
143
144 # If 10th frame has a second roll
145 if tenth_frame_start + 1 < len(self.rolls):
146 tenth_frame_second = self.rolls[tenth_frame_start + 1]
147 # If 10th frame is a spare
148 if tenth_frame_first + tenth_frame_second == 10:
149 # Need exactly 1 fill ball
150 return len(self.rolls) == tenth_frame_start + 3
151 else:
152 # Open frame in 10th - no fill balls needed
153 return len(self.rolls) == tenth_frame_start + 2
154
155 return False
156
157 def _find_tenth_frame_start(self):
158 """
159 Find the starting index of the 10th frame.
160
161 Returns:
162 int: Index of the first roll in the 10th frame, or None if not found.
163 """
164 frame_count = 0
165 roll_index = 0
166
167 while roll_index < len(self.rolls) and frame_count < 9:
168 if self.rolls[roll_index] == 10: # Strike
169 frame_count += 1
170 roll_index += 1
171 else:
172 # Need two rolls for a non-strike frame
173 if roll_index + 1 < len(self.rolls):
174 frame_count += 1
175 roll_index += 2
176 else:
177 return None
178
179 return roll_index if frame_count == 9 else None
180
181 def is_strike(self, roll_index):
182 """
183 Check if a roll is a strike.
184
185 Args:
186 roll_index (int): Index of the roll to check.
187
188 Returns:
189 bool: True if the roll is a strike, False otherwise.
190 """
191 return self.rolls[roll_index] == 10
192
193 def is_spare(self, roll_index):
194 """
195 Check if a frame is a spare.
196
197 Args:
198 roll_index (int): Index of the first roll in the frame.
199
200 Returns:
201 bool: True if the frame is a spare, False otherwise.
202 """
203 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
204
205 def strike_bonus(self, roll_index):
206 """
207 Calculate the bonus for a strike.
208
209 Args:
210 roll_index (int): Index of the strike roll.
211
212 Returns:
213 int: The bonus points for the strike.
214 """
215 # Edge Case: Not enough rolls for bonus calculation
216 if roll_index + 2 >= len(self.rolls):
217 raise ValueError("Not enough rolls to calculate score")
218
219 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
220
221 def spare_bonus(self, roll_index):
222 """
223 Calculate the bonus for a spare.
224
225 Args:
226 roll_index (int): Index of the first roll in the spare frame.
227
228 Returns:
229 int: The bonus points for the spare.
230 """
231 # Edge Case: Not enough rolls for bonus calculation
232 if roll_index + 2 >= len(self.rolls):
233 raise ValueError("Not enough rolls to calculate score")
234
235 return self.rolls[roll_index + 2]
236
237 def sum_of_balls_in_frame(self, roll_index):
238 """
239 Calculate the sum of pins knocked down in a frame.
240
241 Args:
242 roll_index (int): Index of the first roll in the frame.
243
244 Returns:
245 int: The sum of pins knocked down in the frame.
246 """
247 return self.rolls[roll_index] + self.rolls[roll_index + 1]
248
249 # Handled Edge Cases: Invalid pin count, game already over, frame pin sum exceeding 10,
250 # game not complete for scoring, strike/spare in tenth frame,
251 # insufficient rolls for bonus calculation
Test NameStatus
test_a_spare_in_the_last_frame_gets_a_one_roll_bonus_that_is_counted_once
Fail
test_a_strike_with_the_one_roll_bonus_after_a_spare_in_the_last_frame_does_not_get_a_bonus
Fail
test_cannot_roll_after_bonus_roll_for_spare
Fail
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_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_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_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.