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: Too many pins in a frame (only for frames 1-9 and first two rolls of 10th frame)
24 if self._is_second_roll_in_frame() and self.rolls[-1] + pins > 10:
25 raise ValueError("Cannot knock down more than 10 pins in a frame")
26
27 # Edge Case: Game already complete
28 if self._is_game_complete():
29 raise IndexError("Game has already ended")
30
31 self.rolls.append(pins)
32 self.current_roll += 1
33
834 def score(self):
9 pass
35 """
36 Calculate the total score for the game.
37
38 Returns:
39 int: The total score for the game.
40
41 Raises:
42 ValueError: If the game is not yet complete.
43 """
44 # Edge Case: Game not complete
45 if not self._is_game_complete():
46 raise IndexError("Game is not yet complete")
47
48 score = 0
49 roll_index = 0
50
51 # Process frames 1-9
52 for frame in range(9):
53 # Edge Case: Strike
54 if self._is_strike(roll_index):
55 score += 10 + self._strike_bonus(roll_index)
56 roll_index += 1
57 # Edge Case: Spare
58 elif self._is_spare(roll_index):
59 score += 10 + self._spare_bonus(roll_index)
60 roll_index += 2
61 # Edge Case: Open frame
62 else:
63 score += self.rolls[roll_index] + self.rolls[roll_index + 1]
64 roll_index += 2
65
66 # Process frame 10 (special case)
67 if self._is_strike(roll_index):
68 # Strike in 10th frame
69 score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
70 elif self._is_spare(roll_index):
71 # # Spare in 10th frame
72 score += 10 + self.rolls[roll_index + 2]
73 else:
74 # # Open frame in 10th frame
75 score += self.rolls[roll_index] + self.rolls[roll_index + 1]
76
77 return score
78
79 def _is_game_complete(self):
80 """
81 Check if the game is complete.
82
83 Returns:
84 bool: True if the game is complete, False otherwise.
85 """
86 # Need at least 10 rolls to have a complete game
87 if len(self.rolls) < 10:
88 return False
89
90 # Count frames to determine game completion
91 frame_count = 0
92 roll_index = 0
93
94 # Count frames 1-9
95 while frame_count < 9 and roll_index < len(self.rolls):
96 if roll_index >= len(self.rolls):
97 return False
98
99 if self.rolls[roll_index] == 10: # Strike
100 roll_index += 1
101 else: # Open frame or spare
102 if roll_index + 1 >= len(self.rolls):
103 return False
104 roll_index += 2
105 frame_count += 1
106
107 # If we haven't completed 9 frames, game is not complete
108 if frame_count < 9:
109 return False
110
111 # We're in the 10th frame
112 remaining_rolls = len(self.rolls) - roll_index
113
114 # If no rolls in 10th frame, game is not complete
115 if remaining_rolls == 0:
116 return False
117
118 # Get first roll of 10th frame
119 first_roll = self.rolls[roll_index]
120
121 if first_roll == 10: # Strike in 10th frame
122 # Need exactly 3 rolls (strike + 2 fill balls)
123 return remaining_rolls == 3
124 else:
125 # Not a strike
126 if remaining_rolls == 2:
127 # Check if we have a complete 10th frame
128 if roll_index + 1 < len(self.rolls):
129 second_roll = self.rolls[roll_index + 1]
130 if first_roll + second_roll == 10: # Spare
131 return False # Spare needs a fill ball
132 else: # Open frame
133 return True # Open frame is complete
134 else:
135 return False
136 elif remaining_rolls == 3:
137 # Must be a spare with fill ball
138 if roll_index + 1 < len(self.rolls):
139 second_roll = self.rolls[roll_index + 1]
140 return first_roll + second_roll == 10 # Complete only if it's a spare
141 else:
142 return False
143 else:
144 return False # Wrong number of 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 return self.rolls[roll_index] == 10
159
160 def _is_spare(self, roll_index):
161 """
162 Check if a frame is a spare.
163
164 Args:
165 roll_index (int): Index of the first roll in the frame.
166
167 Returns:
168 bool: True if the frame is a spare, False otherwise.
169 """
170 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
171
172 def _strike_bonus(self, roll_index):
173 """
174 Calculate the bonus for a strike.
175
176 Args:
177 roll_index (int): Index of the strike roll.
178
179 Returns:
180 int: The bonus points for the strike.
181 """
182 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
183
184 def _spare_bonus(self, roll_index):
185 """
186 Calculate the bonus for a spare.
187
188 Args:
189 roll_index (int): Index of the first roll in the spare frame.
190
191 Returns:
192 int: The bonus points for the spare.
193 """
194 return self.rolls[roll_index + 2]
195
196 def _is_second_roll_in_frame(self):
197 """
198 Check if the current roll is the second roll in a frame.
199
200 Returns:
201 bool: True if the current roll is the second roll in a frame, False otherwise.
202 """
203 # For the first roll, it's never the second roll in a frame
204 if self.current_roll == 0:
205 return False
206
207 # Count frames to determine where we are
208 frame_count = 0
209 roll_index = 0
210
211 # Process frames 1-9
212 while frame_count < 9 and roll_index < len(self.rolls):
213 if self.rolls[roll_index] == 10: # Strike
214 # If we're checking the current roll position
215 if roll_index + 1 == self.current_roll:
216 return False # First roll of a new frame
217 roll_index += 1
218 else: # Open frame or spare
219 # If we're checking the current roll position
220 if roll_index + 1 == self.current_roll:
221 return True # Second roll of current frame
222 elif roll_index + 2 == self.current_roll:
223 return False # First roll of a new frame
224 roll_index += 2
225 frame_count += 1
226
227 # We're in the 10th frame
228 # In the 10th frame, fill balls don't need validation
229 # Only validate the first two rolls of the 10th frame
230 # Add 1 because self.current_roll is 0-indexed but we want to count actual rolls
231 rolls_in_tenth_frame = (self.current_roll + 1) - roll_index
232
233 # In the 10th frame:
234 # - 1 roll: First roll of 10th frame (no validation needed)
235 # - 2 rolls: Second roll of 10th frame (needs validation ONLY if first roll wasn't a strike)
236 # - 3+ rolls: Fill balls (no validation needed)
237
238 # For fill balls (3rd roll and beyond in 10th frame), no validation
239 if rolls_in_tenth_frame >= 3:
240 return False
241
242 # For second roll in 10th frame, validate only if first roll wasn't a strike
243 if rolls_in_tenth_frame == 2:
244 # Check if first roll in 10th frame was a strike
245 first_roll_in_tenth = self.rolls[roll_index]
246 if first_roll_in_tenth == 10:
247 return False # Second roll after strike in 10th frame is a fill ball, no validation
248 return True # Second roll in regular 10th frame, needs validation
249
250 return False
251
252 # Handled Edge Cases: Invalid pin count, too many pins in a frame, game already complete, game not complete, tenth frame is open, tenth frame is spare or strike
253 # Handled Edge Cases: Invalid pin count, game already complete
254 # Handled Edge Cases: Game not complete
255 # Handled Edge Cases: Not enough rolls, tenth frame is open, tenth frame is spare or strike
256 # Handled Edge Cases: Strike in frames 1-9, Spare in frames 1-9, Open frame in frames 1-9
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.