let it go

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 already completed
24 if self.is_game_complete():
25 raise IndexError("Game is already complete")
26
27 self.rolls.append(pins)
28
29 # Edge Case: First roll in frame is valid, but second roll makes total > 10
30 # But only for frames 1-9, as 10th frame has special rules
31 frame_count = self.get_frame_count()
32 if frame_count < 10 and len(self.rolls) % 2 == 0 and not self.is_strike(len(self.rolls) - 2):
33 if self.rolls[-2] + self.rolls[-1] > 10:
34 raise ValueError("Cannot knock down more than 10 pins in a frame")
35
836 def score(self):
9 pass
37 """
38 Calculate the total score for the game.
39
40 Returns:
41 int: The total score of the game.
42
43 Raises:
44 IndexError: If the game is not yet complete.
45 """
46 # Edge Case: Game not complete
47 if not self.is_game_complete():
48 raise IndexError("Game is not yet complete")
49
50 total_score = 0
51 roll_index = 0
52
53 for frame in range(10):
54 # Handle 10th frame specially
55 if frame == 9: # 10th frame
56 # For the 10th frame, we just sum all the rolls in it
57 if roll_index < len(self.rolls):
58 total_score += self.rolls[roll_index]
59 if roll_index + 1 < len(self.rolls):
60 total_score += self.rolls[roll_index + 1]
61 if roll_index + 2 < len(self.rolls):
62 total_score += self.rolls[roll_index + 2]
63 # Edge Case: Strike in frames 1-9
64 elif self.is_strike(roll_index):
65 total_score += 10 + self.strike_bonus(roll_index)
66 roll_index += 1
67 # Edge Case: Spare in frames 1-9
68 elif self.is_spare(roll_index):
69 total_score += 10 + self.spare_bonus(roll_index)
70 roll_index += 2
71 # Edge Case: Open frame in frames 1-9
72 else:
73 total_score += self.sum_of_balls_in_frame(roll_index)
74 roll_index += 2
75
76 return total_score
77
78 def is_game_complete(self):
79 """
80 Check if the game is complete.
81
82 Returns:
83 bool: True if the game is complete, False otherwise.
84 """
85 # Need at least 10 frames worth of rolls
86 if len(self.rolls) < 10:
87 return False
88
89 # Count frames properly
90 roll_index = 0
91 frame_count = 0
92
93 while frame_count < 10 and roll_index < len(self.rolls):
94 # Handle frames 1-9
95 if frame_count < 9:
96 if self.is_strike(roll_index):
97 roll_index += 1
98 else:
99 roll_index += 2
100 # Handle frame 10
101 else: # frame_count == 9 (10th frame)
102 # In the 10th frame, we can have 2 or 3 rolls depending on strikes/spares
103 # If first roll is a strike, we need 2 more rolls
104 if self.is_strike(roll_index):
105 # Check if we have 2 more rolls
106 if roll_index + 2 < len(self.rolls):
107 roll_index += 3
108 else:
109 return False
110 # If first roll is not a strike, check if second roll makes a spare
111 elif roll_index + 1 < len(self.rolls):
112 if self.rolls[roll_index] + self.rolls[roll_index + 1] == 10:
113 # Spare - need one more roll
114 if roll_index + 2 < len(self.rolls):
115 roll_index += 3
116 else:
117 return False
118 else:
119 # Open frame - only 2 rolls
120 if roll_index + 1 < len(self.rolls):
121 roll_index += 2
122 else:
123 return False
124 else:
125 # Only first roll available
126 return False
127 frame_count += 1
128
129 # Game is complete if we've processed exactly 10 frames and used all rolls
130 return frame_count == 10 and roll_index == len(self.rolls)
131
132 def is_strike(self, roll_index):
133 """
134 Check if a roll is a strike.
135
136 Args:
137 roll_index (int): Index of the roll to check.
138
139 Returns:
140 bool: True if the roll is a strike, False otherwise.
141 """
142 return roll_index < len(self.rolls) and self.rolls[roll_index] == 10
143
144 def is_spare(self, roll_index):
145 """
146 Check if a frame is a spare.
147
148 Args:
149 roll_index (int): Index of the first roll in the frame.
150
151 Returns:
152 bool: True if the frame is a spare, False otherwise.
153 """
154 return (roll_index + 1 < len(self.rolls) and
155 self.rolls[roll_index] + self.rolls[roll_index + 1] == 10)
156
157 def strike_bonus(self, roll_index):
158 """
159 Calculate the bonus for a strike.
160
161 Args:
162 roll_index (int): Index of the strike roll.
163
164 Returns:
165 int: The bonus points for the strike.
166 """
167 # Edge Case: Not enough rolls for bonus calculation
168 if roll_index + 2 >= len(self.rolls):
169 return 0
170 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
171
172 def spare_bonus(self, roll_index):
173 """
174 Calculate the bonus for a spare.
175
176 Args:
177 roll_index (int): Index of the first roll in the spare frame.
178
179 Returns:
180 int: The bonus points for the spare.
181 """
182 # Edge Case: Not enough rolls for bonus calculation
183 if roll_index + 2 >= len(self.rolls):
184 return 0
185 return self.rolls[roll_index + 2]
186
187 def sum_of_balls_in_frame(self, roll_index):
188 """
189 Calculate the sum of pins knocked down in a frame.
190
191 Args:
192 roll_index (int): Index of the first roll in the frame.
193
194 Returns:
195 int: The sum of pins knocked down in the frame.
196 """
197 # Edge Case: Not enough rolls in frame
198 if roll_index + 1 >= len(self.rolls):
199 return self.rolls[roll_index] if roll_index < len(self.rolls) else 0
200 return self.rolls[roll_index] + self.rolls[roll_index + 1]
201
202 def get_frame_count(self):
203 """
204 Calculate the current frame count based on rolls.
205
206 Returns:
207 int: The current frame count (1-10).
208 """
209 roll_index = 0
210 frame_count = 0
211
212 while frame_count < 10 and roll_index < len(self.rolls):
213 # Handle frames 1-9
214 if frame_count < 9:
215 if self.is_strike(roll_index):
216 roll_index += 1
217 else:
218 roll_index += 2
219 # Handle frame 10
220 else:
221 # In the 10th frame, we can have 2 or 3 rolls
222 # Count all remaining rolls as part of the 10th frame
223 roll_index = len(self.rolls) # Exit the loop
224 frame_count += 1
225
226 return frame_count
227
228 # Handled Edge Cases: Invalid pin count, game already completed, invalid pin count in frame, game not complete, strike in frames 1-9, spare in frames 1-9, open frame in frames 1-9, game completion check, not enough rolls for bonus calculation, not enough rolls in frame
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.