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 or more than 10)
20 if pins < 0:
21 raise ValueError("Pins must be between 0 and 10")
22 if pins > 10:
23 raise ValueError("Pins must be between 0 and 10")
24
25 # Edge Case: Game is already over (10 frames completed)
26 if self.is_game_over():
27 raise ValueError("Game is already over")
28
29 # Edge Case: Second roll in frame exceeds 10 pins total (except 10th frame)
30 # Only check if we have a previous roll and we're not in a new frame
31 if (len(self.rolls) > 0 and
32 len(self.rolls) % 2 == 1 and
33 not self.is_strike(len(self.rolls) - 1) and # Previous roll wasn't a strike
34 not self.is_tenth_frame() and
35 self.rolls[-1] + pins > 10):
36 raise ValueError("Cannot knock down more than 10 pins in a frame")
37
38 self.rolls.append(pins)
39
840 def score(self):
9 pass
41 """
42 Calculate the total score for the game.
43
44 Returns:
45 int: The total score of the game.
46
47 Raises:
48 ValueError: If the game is not yet complete.
49 """
50 # Edge Case: Game is not complete yet
51 if not self.is_game_over():
52 raise ValueError("Game is not yet complete")
53
54 score = 0
55 roll_index = 0
56
57 for frame in range(10):
58 # Edge Case: Strike in regular frames (1-9)
59 if self.is_strike(roll_index) and frame < 9:
60 score += 10 + self.strike_bonus(roll_index)
61 roll_index += 1
62 # Edge Case: Spare in regular frames (1-9)
63 elif self.is_spare(roll_index) and frame < 9:
64 score += 10 + self.spare_bonus(roll_index)
65 roll_index += 2
66 # Edge Case: Open frame in regular frames (1-9)
67 elif frame < 9:
68 score += self.sum_of_balls_in_frame(roll_index)
69 roll_index += 2
70 # Edge Case: Tenth frame handling
71 else: # frame == 9
72 # Edge Case: Strike in tenth frame
73 if self.is_strike(roll_index):
74 # Edge Case: Invalid fill balls for strike in tenth frame
75 if len(self.rolls) < roll_index + 3:
76 raise ValueError("game not yet complete")
77 score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
78 # Edge Case: Spare in tenth frame
79 elif self.is_spare(roll_index):
80 # Edge Case: Invalid fill balls for spare in tenth frame
81 if len(self.rolls) < roll_index + 3:
82 raise ValueError("game not yet complete")
83 score += 10 + self.rolls[roll_index + 2]
84 # Edge Case: Open tenth frame
85 else:
86 # Edge Case: Too many rolls in open tenth frame
87 if roll_index + 2 > len(self.rolls):
88 raise ValueError("game not yet complete")
89 score += self.sum_of_balls_in_frame(roll_index)
90
91 return score
92
93 def is_game_over(self):
94 """
95 Check if the game is over (10 frames completed).
96
97 Returns:
98 bool: True if the game is over, False otherwise.
99 """
100 # Count the frames
101 frame_count = 0
102 roll_index = 0
103
104 # Process first 9 frames
105 while frame_count < 9 and roll_index < len(self.rolls):
106 frame_count += 1
107 if self.is_strike(roll_index):
108 roll_index += 1
109 else:
110 roll_index += 2
111
112 # Process 10th frame
113 if frame_count == 9 and roll_index < len(self.rolls):
114 frame_count += 1
115 # If 10th frame is a strike
116 if self.is_strike(roll_index):
117 # Need two more rolls
118 if roll_index + 2 < len(self.rolls):
119 roll_index += 3
120 else:
121 return False # Not enough rolls
122 # If 10th frame is a spare
123 elif (roll_index + 1 < len(self.rolls) and
124 self.rolls[roll_index] + self.rolls[roll_index + 1] == 10):
125 # Need one more roll
126 if roll_index + 2 < len(self.rolls):
127 roll_index += 3
128 else:
129 return False # Not enough rolls
130 # If 10th frame is open
131 else:
132 if roll_index + 1 < len(self.rolls):
133 roll_index += 2
134 else:
135 return False # Not enough rolls
136
137 return frame_count >= 10
138
139 def is_strike(self, roll_index):
140 """
141 Check if a roll is a strike.
142
143 Args:
144 roll_index (int): Index of the roll to check.
145
146 Returns:
147 bool: True if the roll is a strike, False otherwise.
148 """
149 # Edge Case: Not enough rolls to determine if it's a strike
150 if roll_index >= len(self.rolls):
151 return False
152 return self.rolls[roll_index] == 10
153
154 def is_spare(self, roll_index):
155 """
156 Check if a frame is a spare.
157
158 Args:
159 roll_index (int): Index of the first roll in the frame.
160
161 Returns:
162 bool: True if the frame is a spare, False otherwise.
163 """
164 # Edge Case: Not enough rolls to determine if it's a spare
165 if roll_index + 1 >= len(self.rolls):
166 return False
167 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
168
169 def strike_bonus(self, roll_index):
170 """
171 Calculate the bonus for a strike.
172
173 Args:
174 roll_index (int): Index of the strike roll.
175
176 Returns:
177 int: The bonus points for the strike.
178 """
179 # Edge Case: Not enough rolls to calculate strike bonus
180 if roll_index + 2 >= len(self.rolls):
181 return 0
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 # Edge Case: Not enough rolls to calculate spare bonus
195 if roll_index + 2 >= len(self.rolls):
196 return 0
197 return self.rolls[roll_index + 2]
198
199 def sum_of_balls_in_frame(self, roll_index):
200 """
201 Calculate the sum of pins knocked down in a frame.
202
203 Args:
204 roll_index (int): Index of the first roll in the frame.
205
206 Returns:
207 int: The sum of pins knocked down in the frame.
208 """
209 # Edge Case: Not enough rolls to calculate frame sum
210 if roll_index + 1 >= len(self.rolls):
211 return 0
212 return self.rolls[roll_index] + self.rolls[roll_index + 1]
213
214 def is_tenth_frame(self):
215 """
216 Check if we are currently in the tenth frame.
217
218 Returns:
219 bool: True if we are in the tenth frame, False otherwise.
220 """
221 # Count completed frames
222 roll_index = 0
223 frame_count = 0
224
225 while frame_count < 10 and roll_index < len(self.rolls):
226 frame_count += 1
227
228 # If this is a strike in frames 1-9
229 if self.is_strike(roll_index) and frame_count < 10:
230 roll_index += 1
231 # If this is a spare or open frame in frames 1-9
232 elif frame_count < 10:
233 roll_index += 2
234 # If this would be the tenth frame
235 else:
236 break
237
238 # We are in the tenth frame if we have completed 9 frames
239 # and have started but not completed the tenth frame
240 return frame_count == 10 and roll_index < len(self.rolls)
241
242 # Handled Edge Cases: Invalid pin count, game already complete, game not yet complete,
243 # strike in regular frames, spare in regular frames, open frame in regular frames,
244 # strike in tenth frame, spare in tenth frame, open tenth frame, invalid fill balls for strike,
245 # invalid fill balls for spare, too many rolls in open tenth frame, not enough rolls to determine strike,
246 # not enough rolls to determine spare, not enough rolls to calculate strike bonus,
247 # not enough rolls to calculate spare bonus, not enough rolls to calculate frame sum
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.