sn62

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 number of pins (negative)
20 if pins < 0:
21 raise ValueError("Negative roll is invalid")
22
23 # Edge Case: Invalid number of pins (more than 10)
24 if pins > 10:
25 raise ValueError("Pin count exceeds pins on the lane")
26
27 # Edge Case: Game already has 10 frames and no more rolls are allowed
28 if self.current_roll >= 20 and not self._is_tenth_frame_open():
29 raise ValueError("Cannot roll after game is over")
30
31 # Edge Case: Invalid roll in frame (trying to knock down more pins than remaining)
32 if self.current_roll % 2 == 1: # Second roll in a frame
33 # Check if this is a valid second roll (not exceeding 10 pins in frame)
34 if self.current_roll > 0:
35 first_roll_in_frame = self.rolls[self.current_roll - 1]
36 if first_roll_in_frame != 10 and first_roll_in_frame + pins > 10:
37 raise ValueError("Pin count exceeds pins on the lane")
38
39 # Edge Case: Tenth frame special rules
40 if self.current_roll >= 18: # In the tenth frame
41 if self.current_roll == 18: # First roll of tenth frame
42 self.rolls.append(pins)
43 self.current_roll += 1
44 elif self.current_roll == 19: # Second roll of tenth frame
45 # Check if second roll is valid
46 if self.rolls[18] == 10: # First was a strike
47 # Second roll can be anything 0-10
48 self.rolls.append(pins)
49 self.current_roll += 1
50 else: # First was not a strike
51 # Sum of first two rolls cannot exceed 10
52 if self.rolls[18] + pins > 10:
53 raise ValueError("Pin count exceeds pins on the lane")
54 self.rolls.append(pins)
55 self.current_roll += 1
56 elif self.current_roll == 20: # First fill ball
57 # Only allowed if previous frame was spare or strike
58 if self.rolls[18] == 10 or (self.rolls[18] + self.rolls[19] == 10):
59 self.rolls.append(pins)
60 self.current_roll += 1
61 else:
62 raise ValueError("Cannot roll after game is over")
63 elif self.current_roll == 21: # Second fill ball
64 # Only allowed if first fill ball was a strike or tenth frame was XXX
65 if self.rolls[18] == 10 and self.rolls[19] == 10:
66 self.rolls.append(pins)
67 self.current_roll += 1
68 elif self.rolls[18] == 10 and self.rolls[20] == 10:
69 self.rolls.append(pins)
70 self.current_roll += 1
71 else:
72 raise ValueError("Cannot roll after game is over")
73 else:
74 self.rolls.append(pins)
75 self.current_roll += 1
76
877 def score(self):
9 pass
78 """
79 Calculate the total score for the game.
80
81 Returns:
82 int: The total score of the game.
83
84 Raises:
85 ValueError: If the game is not yet complete.
86 """
87 # Edge Case: Game is not complete
88 if not self._is_game_complete():
89 raise ValueError("Score cannot be taken until the end of the game")
90
91 total_score = 0
92 roll_index = 0
93
94 for frame in range(10):
95 if self._is_strike(roll_index): # Strike
96 total_score += 10 + self._strike_bonus(roll_index)
97 roll_index += 1
98 elif self._is_spare(roll_index): # Spare
99 total_score += 10 + self._spare_bonus(roll_index)
100 roll_index += 2
101 else: # Open frame
102 total_score += self.rolls[roll_index] + self.rolls[roll_index + 1]
103 roll_index += 2
104
105 return total_score
106
107 def _is_strike(self, roll_index):
108 """
109 Check if a roll is a strike.
110
111 Args:
112 roll_index (int): Index of the roll to check.
113
114 Returns:
115 bool: True if the roll is a strike, False otherwise.
116 """
117 return self.rolls[roll_index] == 10
118
119 def _is_spare(self, roll_index):
120 """
121 Check if a frame is a spare.
122
123 Args:
124 roll_index (int): Index of the first roll in the frame.
125
126 Returns:
127 bool: True if the frame is a spare, False otherwise.
128 """
129 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
130
131 def _strike_bonus(self, roll_index):
132 """
133 Calculate the bonus for a strike.
134
135 Args:
136 roll_index (int): Index of the strike roll.
137
138 Returns:
139 int: The bonus points for the strike.
140 """
141 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
142
143 def _spare_bonus(self, roll_index):
144 """
145 Calculate the bonus for a spare.
146
147 Args:
148 roll_index (int): Index of the first roll in the spare frame.
149
150 Returns:
151 int: The bonus points for the spare.
152 """
153 return self.rolls[roll_index + 2]
154
155 def _is_game_complete(self):
156 """
157 Check if the game is complete.
158
159 Returns:
160 bool: True if the game is complete, False otherwise.
161 """
162 # A game is complete when we have 10 frames worth of rolls,
163 # plus any necessary fill balls for the tenth frame
164 if len(self.rolls) < 18: # Less than 9 complete frames
165 return False
166
167 if len(self.rolls) == 18: # 9 complete frames, need to check 10th
168 # If 10th frame is open (not strike or spare), game is complete
169 return self.rolls[18] + self.rolls[19] < 10
170
171 if len(self.rolls) == 19: # First roll of 10th frame is done
172 # If first roll was a strike, need one more roll
173 return self.rolls[18] != 10
174
175 if len(self.rolls) == 20: # First two rolls of 10th frame are done
176 # If we had a strike or spare, we need fill balls
177 return not (self.rolls[18] == 10 or self.rolls[18] + self.rolls[19] == 10)
178
179 if len(self.rolls) == 21: # One fill ball done
180 # If 10th frame was XXX, we need one more fill ball
181 return not (self.rolls[18] == 10 and self.rolls[19] == 10)
182
183 # 22 rolls means we had XXX in the 10th frame
184 return len(self.rolls) == 22
185
186 def _is_tenth_frame_open(self):
187 """
188 Check if the tenth frame is open (no strike or spare).
189
190 Returns:
191 bool: True if the tenth frame is open, False otherwise.
192 """
193 if len(self.rolls) < 18:
194 return False
195
196 if len(self.rolls) >= 20:
197 # Check if tenth frame is open
198 return self.rolls[18] + self.rolls[19] < 10
199
200 return False
201
202 # Handled Edge Cases: Invalid number of pins (negative), Invalid number of pins (more than 10),
203 # Game already has 10 frames and no more rolls are allowed, Invalid roll in frame (trying to knock down more pins than remaining),
204 # Tenth frame special rules, Game is not complete
205
206 # Handled Edge Cases: Invalid number of pins (negative), Invalid number of pins (more than 10),
207 # Game already has 10 frames and no more rolls are allowed, Invalid roll in frame (trying to knock down more pins than remaining),
208 # Tenth frame special rules
209
210 # Handled Edge Cases: Game is not complete
211
212 # Handled Edge Cases: None (helper method)
213
214 # Handled Edge Cases: None (helper method)
215
216 # Handled Edge Cases: None (helper method)
217
218 # Handled Edge Cases: None (helper method)
219
220 # Handled Edge Cases: Game not complete with less than 18 rolls, Game complete with exactly 18 rolls and open 10th frame,
221 # Game complete with 19 rolls and first roll of 10th frame is not strike, Game complete with 20 rolls and 10th frame is open,
222 # Game complete with 21 rolls and 10th frame is not XXX, Game complete with 22 rolls (XXX in 10th frame)
223
224 # Handled Edge Cases: Tenth frame is not complete with less than 18 rolls, Tenth frame status with 20 rolls,
225 # Tenth frame is open with sufficient rolls
Test NameStatus
test_a_spare_in_the_last_frame_gets_a_one_roll_bonus_that_is_counted_once
Fail
test_a_strike_in_the_last_frame_gets_a_two_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_all_strikes_is_a_perfect_game
Fail
test_cannot_roll_after_bonus_roll_for_spare
Fail
test_cannot_roll_after_bonus_rolls_for_strike
Fail
test_consecutive_strikes_each_get_the_two_roll_bonus
Fail
test_rolling_a_spare_with_the_two_roll_bonus_does_not_get_a_bonus_roll
Fail
test_strikes_with_the_two_roll_bonus_do_not_get_bonus_rolls
Fail
test_two_bonus_rolls_after_a_strike_in_the_last_frame_can_score_more_than_10_points_if_one_is_a_strike
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_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_if_game_already_has_ten_frames
Pass
test_consecutive_spares_each_get_a_one_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_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_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_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.