Alone

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
9 def get_current_frame(self):
10 """
11 Determine which frame we are currently in (1-10).
12
13 Returns:
14 int: The current frame number (1-10).
15 """
16 if not self.rolls:
17 return 1
18
19 roll_index = 0
20 frame = 1
21
22 # Process each frame to see which one we're currently in
23 while frame <= 10:
24 # If we've processed all rolls, we're in the current frame
25 if roll_index >= len(self.rolls):
26 return frame
27
28 # Tenth frame special case
29 if frame == 10:
30 # In the tenth frame, we can have 2 or 3 rolls
31 # If we have more rolls, we're still in the tenth frame
32 return 10
33
34 # Regular frames (1-9)
35 # Strike - one roll completes the frame
36 if self.rolls[roll_index] == 10:
37 roll_index += 1
38 # Spare or open frame - two rolls complete the frame
39 elif roll_index + 1 < len(self.rolls):
40 roll_index += 2
41 # We're in the middle of a frame
42 else:
43 return frame
44
45 frame += 1
46
47 return 10
48
49 def get_rolls_in_previous_frames(self, current_frame):
50 """
51 Calculate how many rolls were in previous completed frames.
52
53 Args:
54 current_frame (int): The current frame number (1-10).
55
56 Returns:
57 int: Number of rolls in previous frames.
58 """
59 if current_frame <= 1:
60 return 0
61
62 roll_index = 0
63 frame = 1
64
65 # Count rolls in completed frames
66 while frame < current_frame and frame <= 9:
67 # Strike - one roll completes the frame
68 if self.rolls[roll_index] == 10:
69 roll_index += 1
70 # Spare or open frame - two rolls complete the frame
71 elif roll_index + 1 < len(self.rolls):
72 roll_index += 2
73 else:
74 # Incomplete frame
75 break
76 frame += 1
77
78 return roll_index
79
580 def roll(self, pins):
6 pass
7
81 """
82 Record a roll in the game.
83
84 Args:
85 pins (int): Number of pins knocked down in this roll.
86
87 Raises:
88 ValueError: If the number of pins is invalid.
89 """
90 # Edge Case: Invalid pin count (negative or more than 10)
91 if pins < 0 or pins > 10:
92 if pins < 0:
93 raise ValueError("Pins must be between 0 and 10")
94 else:
95 raise ValueError("Pins must be between 0 and 10")
96
97 # Edge Case: Too many pins in a frame
98 # Check if we are in a regular frame (not 10th frame fill balls)
99 frame_number = self.get_current_frame()
100 if frame_number < 10: # Regular frames (1-9)
101 # Determine if this is the first or second roll of the current frame
102 # Calculate how many rolls were in previous completed frames
103 rolls_in_previous_frames = self.get_rolls_in_previous_frames(frame_number)
104
105 # If this is the second roll of the frame
106 if len(self.rolls) - rolls_in_previous_frames == 1:
107 # Get the first roll of this frame
108 first_roll_of_frame = self.rolls[rolls_in_previous_frames]
109 # If the first roll wasn't a strike, check if the sum exceeds 10
110 if first_roll_of_frame != 10 and first_roll_of_frame + pins > 10:
111 raise ValueError("Cannot knock down more than 10 pins in a frame")
112 # For the tenth frame, we need special handling for fill balls
113 elif frame_number == 10:
114 # In the tenth frame, we need to determine if we're in the regular part or fill balls
115 rolls_in_previous_frames = self.get_rolls_in_previous_frames(frame_number)
116 rolls_in_current_frame = len(self.rolls) - rolls_in_previous_frames
117
118 # If we're in the regular part of the tenth frame (first or second roll)
119 if rolls_in_current_frame < 2:
120 # If this is the second roll of the tenth frame
121 if rolls_in_current_frame == 1:
122 # Get the first roll of this frame
123 first_roll_of_frame = self.rolls[rolls_in_previous_frames]
124 # If the first roll wasn't a strike, check if the sum exceeds 10
125 if first_roll_of_frame != 10 and first_roll_of_frame + pins > 10:
126 raise ValueError("Cannot knock down more than 10 pins in a frame")
127 # If we're in fill balls (third roll or beyond in tenth frame), no validation needed
128
129 # Edge Case: Game already completed
130 if self.is_game_complete():
131 raise ValueError("Game has already ended")
132
133 self.rolls.append(pins)
134 self.current_roll += 1
135
8136 def score(self):
9 pass
137 """
138 Calculate the total score for the game.
139
140 Returns:
141 int: The total score of the game.
142
143 Raises:
144 ValueError: If the game is not yet complete.
145 """
146 # Edge Case: Game not complete
147 if not self.is_game_complete():
148 raise ValueError("Game is not yet complete")
149
150 total_score = 0
151 roll_index = 0
152
153 for frame in range(10):
154 # Edge Case: Strike in regular frames (1-9)
155 if self.rolls[roll_index] == 10: # Strike
156 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
157 roll_index += 1
158 # Edge Case: Spare in regular frames (1-9)
159 elif self.rolls[roll_index] + self.rolls[roll_index + 1] == 10: # Spare
160 total_score += 10 + self.rolls[roll_index + 2]
161 roll_index += 2
162 # Edge Case: Open frame in regular frames (1-9)
163 else: # Open frame
164 total_score += self.rolls[roll_index] + self.rolls[roll_index + 1]
165 roll_index += 2
166
167 return total_score
168
169 def is_game_complete(self):
170 """
171 Check if the game is complete.
172
173 Returns:
174 bool: True if the game is complete, False otherwise.
175 """
176 if len(self.rolls) < 12: # Minimum rolls needed (12 if all strikes)
177 return False
178
179 # Count frames
180 roll_index = 0
181
182 for frame in range(10):
183 if roll_index >= len(self.rolls):
184 return False
185
186 # Tenth frame special case
187 if frame == 9:
188 # Edge Case: Tenth frame with strike needs 3 rolls (strike + 2 fill balls)
189 if self.rolls[roll_index] == 10:
190 if roll_index + 2 < len(self.rolls):
191 roll_index += 3
192 else:
193 return False
194 # Edge Case: Tenth frame with spare needs 3 rolls (spare + 1 fill ball)
195 elif roll_index + 1 < len(self.rolls) and \
196 self.rolls[roll_index] + self.rolls[roll_index + 1] == 10:
197 if roll_index + 2 < len(self.rolls):
198 roll_index += 3
199 else:
200 return False
201 # Edge Case: Tenth frame is open (only 2 rolls)
202 else:
203 if roll_index + 1 < len(self.rolls):
204 roll_index += 2
205 else:
206 return False
207 # Regular frames (1-9)
208 else:
209 # Edge Case: Strike
210 if self.rolls[roll_index] == 10:
211 roll_index += 1
212 # Edge Case: Spare or open frame
213 else:
214 if roll_index + 1 < len(self.rolls):
215 roll_index += 2
216 else:
217 return False
218
219 # Edge Case: Too many rolls
220 if roll_index < len(self.rolls):
221 return False
222
223 return True
224
225 # Handled Edge Cases: Invalid pin count (negative or more than 10), too many pins in a frame, game already completed, game not complete, strike in regular frames, spare in regular frames, open frame in regular frames, tenth frame with strike or spare needs 3 rolls, tenth frame is open (only 2 rolls), too many rolls
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.