\besttop

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 pins is negative or greater than 10
18 IndexError: If trying to roll after the game is over
19 """
20 # Edge Case: Invalid number of pins (negative or more than 10)
21 if pins < 0 or pins > 10:
22 raise ValueError("invalid pin count")
23
24 # Edge Case: Trying to roll after game is over
25 if self.is_game_over():
26 raise ValueError("game already ended")
27
28 # Edge Case: In a frame, cannot knock down more pins than remaining
29 # We need to determine if this is the second roll of a frame and validate accordingly
30 if len(self.rolls) > 0:
31 # Count completed frames to determine if we're in a new frame
32 completed_frames = 0
33 i = 0
34 while i < len(self.rolls):
35 # If this is a strike (first roll of frame is 10)
36 if self.rolls[i] == 10:
37 completed_frames += 1
38 i += 1
39 else:
40 # Regular frame - need two rolls
41 if i + 1 < len(self.rolls):
42 completed_frames += 1
43 i += 2
44 else:
45 # This is the second roll of current frame
46 if self.rolls[i] + pins > 10:
47 raise ValueError("invalid pin count")
48 break
49
50 self.rolls.append(pins)
51
852 def score(self):
9 pass
53 """
54 Calculate the total score for the game.
55
56 Returns:
57 int: The total score for the game
58
59 Raises:
60 IndexError: If the game is not yet complete
61 """
62 # Edge Case: Trying to score before game is complete
63 if not self.is_game_over():
64 raise ValueError("game not yet ended")
65
66 score = 0
67 roll_index = 0
68
69 for frame in range(10):
70 # Edge Case: Strike (first roll of frame knocks down all 10 pins)
71 if self.is_strike(roll_index):
72 score += 10 + self.strike_bonus(roll_index)
73 roll_index += 1
74 # Edge Case: Spare (both rolls of frame knock down all 10 pins)
75 elif self.is_spare(roll_index):
76 score += 10 + self.spare_bonus(roll_index)
77 roll_index += 2
78 # Edge Case: Open frame (less than 10 pins knocked down in frame)
79 else:
80 score += self.sum_of_balls_in_frame(roll_index)
81 roll_index += 2
82
83 return score
84
85 def is_game_over(self):
86 """
87 Check if the game is over.
88
89 Returns:
90 bool: True if the game is over, False otherwise
91 """
92 # Count completed frames
93 frame_count = 0
94 roll_index = 0
95
96 while roll_index < len(self.rolls) and frame_count < 10:
97 if roll_index < len(self.rolls) and self.rolls[roll_index] == 10:
98 # Strike - one roll frame
99 frame_count += 1
100 roll_index += 1
101 else:
102 # Regular frame - need two rolls
103 if roll_index + 1 < len(self.rolls):
104 frame_count += 1
105 roll_index += 2
106 else:
107 # Incomplete frame
108 break
109
110 # If we haven't completed 10 frames, game is not over
111 if frame_count < 10:
112 return False
113
114 # If we have exactly 10 frames completed
115 if frame_count == 10:
116 # Need to check if the 10th frame needs bonus balls
117 # Find where the 10th frame starts
118 temp_frame_count = 0
119 tenth_frame_start = 0
120 i = 0
121
122 while i < len(self.rolls) and temp_frame_count < 10:
123 if self.rolls[i] == 10:
124 # Strike frame
125 temp_frame_count += 1
126 if temp_frame_count == 10:
127 tenth_frame_start = i
128 break
129 i += 1
130 else:
131 # Regular frame
132 temp_frame_count += 1
133 if temp_frame_count == 10:
134 tenth_frame_start = i
135 break
136 if i + 1 < len(self.rolls):
137 i += 2
138 else:
139 # Incomplete 10th frame
140 return False
141
142 # Now check the 10th frame
143 if tenth_frame_start < len(self.rolls):
144 # If 10th frame is a strike
145 if self.rolls[tenth_frame_start] == 10:
146 # Need 2 bonus rolls
147 bonus_rolls_count = 0
148 for i in range(tenth_frame_start + 1, len(self.rolls)):
149 bonus_rolls_count += 1
150 return bonus_rolls_count >= 2
151 # If 10th frame is a spare
152 elif tenth_frame_start + 1 < len(self.rolls) and self.rolls[tenth_frame_start] + self.rolls[tenth_frame_start + 1] == 10:
153 # Need 1 bonus roll
154 bonus_rolls_count = 0
155 for i in range(tenth_frame_start + 2, len(self.rolls)):
156 bonus_rolls_count += 1
157 return bonus_rolls_count >= 1
158 # If 10th frame is open
159 else:
160 return True
161
162 return True
163
164 def is_strike(self, roll_index):
165 """
166 Check if a roll is a strike.
167
168 Args:
169 roll_index (int): Index of the roll to check
170
171 Returns:
172 bool: True if the roll is a strike, False otherwise
173 """
174 return self.rolls[roll_index] == 10
175
176 def is_spare(self, roll_index):
177 """
178 Check if a frame is a spare.
179
180 Args:
181 roll_index (int): Index of the first roll of the frame
182
183 Returns:
184 bool: True if the frame is a spare, False otherwise
185 """
186 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
187
188 def sum_of_balls_in_frame(self, roll_index):
189 """
190 Calculate the sum of pins knocked down in a frame.
191
192 Args:
193 roll_index (int): Index of the first roll of the frame
194
195 Returns:
196 int: Sum of pins knocked down in the frame
197 """
198 return self.rolls[roll_index] + self.rolls[roll_index + 1]
199
200 def spare_bonus(self, roll_index):
201 """
202 Calculate the bonus for a spare.
203
204 Args:
205 roll_index (int): Index of the first roll of the spare frame
206
207 Returns:
208 int: The bonus points for the spare
209 """
210 return self.rolls[roll_index + 2]
211
212 def strike_bonus(self, roll_index):
213 """
214 Calculate the bonus for a strike.
215
216 Args:
217 roll_index (int): Index of the strike roll
218
219 Returns:
220 int: The bonus points for the strike
221 """
222 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
223
224 # Handled Edge Cases: Invalid pins count, rolling after game over, invalid fill balls, scoring before game completion, strike frames, spare frames, open frames
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.