who-are-you

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 self.rolls = []
4
55 def roll(self, pins):
6 pass
7
6 if pins < 0:
7 raise ValueError("Pins must be between 0 and 10")
8 if pins > 10:
9 raise ValueError("Pins must be between 0 and 10")
10
11 # Check if game would be over after this roll
12 temp_rolls = self.rolls + [pins]
13 if self._would_be_game_over(temp_rolls):
14 raise ValueError("Game is over")
15
16 # Validate frame constraints
17 self._validate_roll(pins)
18
19 self.rolls.append(pins)
20
21 def _would_be_game_over(self, rolls):
22 """Check if the game would be over with the given rolls."""
23 if len(rolls) < 12: # Minimum rolls for a complete game
24 return False
25
26 frame_count = 0
27 roll_index = 0
28
29 # Count first 9 frames
30 while frame_count < 9 and roll_index < len(rolls):
31 if rolls[roll_index] == 10: # Strike
32 frame_count += 1
33 roll_index += 1
34 else:
35 # Need two rolls for a non-strike frame
36 if roll_index + 1 < len(rolls):
37 frame_count += 1
38 roll_index += 2
39 else:
40 # Incomplete frame
41 return False
42
43 if frame_count < 9:
44 return False
45
46 # Check frame 10
47 if roll_index >= len(rolls):
48 return False
49
50 # Frame 10 logic
51 remaining_rolls = len(rolls) - roll_index
52 if rolls[roll_index] == 10: # Strike in frame 10
53 return remaining_rolls > 3
54 elif roll_index + 1 < len(rolls) and rolls[roll_index] + rolls[roll_index + 1] == 10: # Spare in frame 10
55 return remaining_rolls > 3
56 else: # Open frame in frame 10
57 return remaining_rolls > 2
58
59 def _validate_roll(self, pins):
60 """Validate that the current roll is valid within its frame."""
61 if len(self.rolls) == 0:
62 return
63
64 # Check if we're in frame 10
65 frame_count = 0
66 roll_index = 0
67
68 # Count frames to see if we're in frame 10
69 while frame_count < 9 and roll_index < len(self.rolls):
70 if self.rolls[roll_index] == 10: # Strike
71 frame_count += 1
72 roll_index += 1
73 else:
74 # Need two rolls for a non-strike frame
75 if roll_index + 1 < len(self.rolls):
76 frame_count += 1
77 roll_index += 2
78 else:
79 # Incomplete frame
80 break
81
82 if frame_count >= 9:
83 # We're in frame 10
84 remaining_rolls = len(self.rolls) - roll_index
85 if remaining_rolls == 0:
86 # First roll of frame 10 - no validation needed
87 pass
88 elif remaining_rolls == 1:
89 # Second roll of frame 10
90 if self.rolls[roll_index] == 10:
91 # First roll was strike - no validation for second roll
92 pass
93 else:
94 # First roll was not strike - check if total exceeds 10
95 if self.rolls[roll_index] + pins > 10:
96 raise ValueError("Invalid pin count for frame")
97 elif remaining_rolls == 2:
98 # Third roll of frame 10
99 if self.rolls[roll_index] == 10 and self.rolls[roll_index + 1] == 10:
100 # Two strikes - no validation for third roll
101 pass
102 elif self.rolls[roll_index] == 10 and self.rolls[roll_index + 1] < 10:
103 # Strike followed by non-strike - check if total exceeds 10
104 if self.rolls[roll_index + 1] + pins > 10:
105 raise ValueError("Invalid pin count for frame")
106 # Spare case - no validation needed
107 else:
108 # We're in frames 1-9
109 # Find the current frame we're working on
110 current_frame_start = 0
111 temp_frame_count = 0
112 temp_roll_index = 0
113
114 while temp_frame_count < frame_count and temp_roll_index < len(self.rolls):
115 if self.rolls[temp_roll_index] == 10: # Strike
116 temp_frame_count += 1
117 temp_roll_index += 1
118 else:
119 # Need two rolls for a non-strike frame
120 if temp_roll_index + 1 < len(self.rolls):
121 temp_frame_count += 1
122 temp_roll_index += 2
123 else:
124 break
125
126 current_frame_start = temp_roll_index
127
128 # Check if we're on the second roll of the current frame
129 if current_frame_start < len(self.rolls):
130 if self.rolls[current_frame_start] == 10:
131 # Previous roll was a strike, so this is a new frame
132 pass
133 elif len(self.rolls) - current_frame_start == 1:
134 # We have one roll in the current frame, this is the second roll
135 if self.rolls[current_frame_start] + pins > 10:
136 raise ValueError("Invalid pin count for frame")
137
8138 def score(self):
9 pass
139 # Validate game is complete
140 if not self._is_game_complete():
141 raise ValueError("Game is not complete")
142
143 total_score = 0
144 roll_index = 0
145
146 # Score first 9 frames
147 for frame in range(9):
148 if roll_index >= len(self.rolls):
149 raise ValueError("Game is not complete")
150
151 if self.rolls[roll_index] == 10: # Strike
152 if roll_index + 2 >= len(self.rolls):
153 raise ValueError("Game is not complete")
154 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
155 roll_index += 1
156 else:
157 if roll_index + 1 >= len(self.rolls):
158 raise ValueError("Game is not complete")
159 frame_score = self.rolls[roll_index] + self.rolls[roll_index + 1]
160 if frame_score > 10 and self.rolls[roll_index] != 10:
161 raise ValueError("Invalid frame scores")
162
163 if frame_score == 10: # Spare
164 if roll_index + 2 >= len(self.rolls):
165 raise ValueError("Game is not complete")
166 total_score += 10 + self.rolls[roll_index + 2]
167 else: # Open frame
168 total_score += frame_score
169 roll_index += 2
170
171 # Score frame 10
172 if roll_index >= len(self.rolls):
173 raise ValueError("Game is not complete")
174
175 # Frame 10 can have 2 or 3 rolls
176 frame_10_rolls = self.rolls[roll_index:]
177
178 if len(frame_10_rolls) < 2:
179 raise ValueError("Game is not complete")
180 if len(frame_10_rolls) > 3:
181 raise ValueError("Cannot roll after game is over")
182
183 frame_10_score = sum(frame_10_rolls)
184
185 # Validate frame 10
186 if len(frame_10_rolls) == 2:
187 if frame_10_rolls[0] != 10 and frame_10_rolls[0] + frame_10_rolls[1] > 10:
188 raise ValueError("Invalid frame scores")
189 elif len(frame_10_rolls) == 3:
190 # First roll must be strike or first two must be spare
191 if frame_10_rolls[0] == 10:
192 # First roll is strike
193 if frame_10_rolls[1] == 10:
194 # Second roll is strike - third can be anything 0-10
195 if frame_10_rolls[2] > 10:
196 raise ValueError("Pins must be between 0 and 10")
197 else:
198 # Second roll is not strike
199 if frame_10_rolls[1] + frame_10_rolls[2] > 10 and frame_10_rolls[1] != 10:
200 raise ValueError("Invalid frame scores")
201 elif frame_10_rolls[0] + frame_10_rolls[1] == 10:
202 # First two rolls are spare
203 if frame_10_rolls[2] > 10:
204 raise ValueError("Pins must be between 0 and 10")
205 else:
206 # First two rolls are open frame - shouldn't have third roll
207 raise ValueError("Cannot roll after game is over")
208
209 total_score += frame_10_score
210 return total_score
211
212 def _is_game_complete(self):
213 """Check if the game is complete based on the rolls."""
214 if len(self.rolls) < 12: # Minimum rolls for a complete game (all strikes)
215 return False
216
217 frame_count = 0
218 roll_index = 0
219
220 # Count first 9 frames
221 while frame_count < 9 and roll_index < len(self.rolls):
222 if self.rolls[roll_index] == 10: # Strike
223 frame_count += 1
224 roll_index += 1
225 else:
226 # Need two rolls for a non-strike frame
227 if roll_index + 1 < len(self.rolls):
228 frame_count += 1
229 roll_index += 2
230 else:
231 # Incomplete frame
232 return False
233
234 if frame_count < 9:
235 return False
236
237 # Check frame 10
238 if roll_index >= len(self.rolls):
239 return False
240
241 # Frame 10 logic
242 remaining_rolls = len(self.rolls) - roll_index
243 if self.rolls[roll_index] == 10: # Strike in frame 10
244 return remaining_rolls == 2 or remaining_rolls == 3
245 elif roll_index + 1 < len(self.rolls) and self.rolls[roll_index] + self.rolls[roll_index + 1] == 10: # Spare in frame 10
246 return remaining_rolls == 3
247 else: # Open frame in frame 10
248 return remaining_rolls == 2
Test NameStatus
test_both_bonus_rolls_for_a_strike_in_the_last_frame_must_be_rolled_before_score_can_be_calculated
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_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.