armor

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5 """
6 self.rolls = []
7 self.current_frame = 1
8 self.frame_rolls = []
9 self.frames = []
10
511 def roll(self, pins):
6 pass
7
12 """
13 Record a roll in the game.
14
15 Args:
16 pins (int): Number of pins knocked down in this roll
17
18 Raises:
19 ValueError: If pins is negative or greater than 10
20 IndexError: If game is already over
21 """
22 # Edge Case: Invalid number of pins
23 if pins < 0:
24 raise ValueError("Negative roll is invalid")
25
26 # Edge Case: Too many pins
27 if pins > 10:
28 raise ValueError("Pin count exceeds pins on the lane")
29
30 # Edge Case: Game already completed
31 if len(self.frames) == 10:
32 # Check if we're in the 10th frame and can still roll
33 tenth_frame = self.frames[9]
34 # If it's not a strike or spare, no more rolls allowed
35 if not (tenth_frame[0] == 10 or sum(tenth_frame) >= 10):
36 raise IndexError("Cannot roll after game is over")
37 # If it's a strike or spare, check if we've had enough fill balls
38 elif len(tenth_frame) >= 3 or (tenth_frame[0] != 10 and len(tenth_frame) >= 2):
39 raise IndexError("Cannot roll after game is over")
40
41 # Edge Case: Frame validation in progress
42 if self.current_frame <= 10:
43 self.frame_rolls.append(pins)
44
45 # Handle regular frames (1-9)
46 if self.current_frame < 10:
47 # Strike - move to next frame
48 if self.frame_rolls[0] == 10:
49 self.frames.append(self.frame_rolls)
50 self.current_frame += 1
51 self.frame_rolls = []
52 # Second roll - move to next frame
53 elif len(self.frame_rolls) == 2:
54 # Edge Case: Frame total exceeds 10
55 if sum(self.frame_rolls) > 10:
56 raise ValueError("Pin count exceeds pins on the lane")
57 self.frames.append(self.frame_rolls)
58 self.current_frame += 1
59 self.frame_rolls = []
60 # Handle 10th frame
61 else: # current_frame == 10
62 tenth_frame = self.frame_rolls
63 # First roll is a strike
64 if len(tenth_frame) == 1 and tenth_frame[0] == 10:
65 # Need two more rolls
66 pass
67 # Second roll
68 elif len(tenth_frame) == 2:
69 # If first two rolls are a strike or spare, need one more roll
70 if tenth_frame[0] == 10 or sum(tenth_frame) >= 10:
71 pass
72 else:
73 # Open frame - game over
74 self.frames.append(tenth_frame)
75 # Third roll
76 elif len(tenth_frame) == 3:
77 # Edge Case: Invalid fill balls
78 if tenth_frame[0] == 10: # First roll was a strike
79 # Second roll can be anything, third roll depends on second
80 if tenth_frame[1] < 10 and tenth_frame[1] + tenth_frame[2] > 10:
81 raise ValueError("Pin count exceeds pins on the lane")
82 elif tenth_frame[0] + tenth_frame[1] == 10: # First two rolls were a spare
83 # Third roll can be anything from 0 to 10
84 pass
85 else:
86 # This shouldn't happen based on previous logic
87 raise ValueError("invalid fill balls")
88 self.frames.append(tenth_frame)
89 else:
90 # Edge Case: Roll after game completion
91 raise IndexError("Cannot roll after game is over")
92
93 self.rolls.append(pins)
94
895 def score(self):
9 pass
96 """
97 Calculate the total score for the game.
98
99 Returns:
100 int: The total score for the game
101
102 Raises:
103 IndexError: If the game is not yet complete
104 """
105 # Edge Case: New game with no frames
106 if len(self.frames) == 0:
107 return 0
108
109 # Edge Case: Game not complete
110 if len(self.frames) < 10:
111 raise IndexError("Score cannot be taken until the end of the game")
112
113 # Edge Case: 10th frame incomplete
114 if len(self.frames) == 10 and len(self.frames[9]) < 2:
115 raise IndexError("Score cannot be taken until the end of the game")
116
117 # Edge Case: 10th frame is spare but missing fill ball
118 tenth_frame = self.frames[9]
119 if len(tenth_frame) == 2 and sum(tenth_frame) >= 10:
120 raise IndexError("Score cannot be taken until the end of the game")
121
122 total_score = 0
123
124 # Score first 9 frames
125 for frame_index in range(9):
126 frame = self.frames[frame_index]
127
128 # Strike
129 if frame[0] == 10:
130 total_score += 10 + self._next_two_balls(frame_index)
131 # Spare
132 elif sum(frame) == 10:
133 total_score += 10 + self._next_ball(frame_index)
134 # Open frame
135 else:
136 total_score += sum(frame)
137
138 # Score 10th frame
139 tenth_frame = self.frames[9]
140 total_score += sum(tenth_frame)
141
142 return total_score
143
144 def _next_ball(self, frame_index):
145 """
146 Get the score of the next ball after the given frame.
147
148 Args:
149 frame_index (int): Index of the current frame
150
151 Returns:
152 int: Score of the next ball
153 """
154 if frame_index + 1 < len(self.frames):
155 return self.frames[frame_index + 1][0]
156 return 0
157
158 def _next_two_balls(self, frame_index):
159 """
160 Get the score of the next two balls after the given frame.
161
162 Args:
163 frame_index (int): Index of the current frame
164
165 Returns:
166 int: Score of the next two balls
167 """
168 score = 0
169
170 # If we're not in the last frame
171 if frame_index + 1 < len(self.frames):
172 next_frame = self.frames[frame_index + 1]
173
174 # If next frame is a strike and not the 10th frame
175 if len(next_frame) > 0 and next_frame[0] == 10 and frame_index + 1 < 9:
176 score += 10
177 # Add first ball of frame after next
178 if frame_index + 2 < len(self.frames) and len(self.frames[frame_index + 2]) > 0:
179 score += self.frames[frame_index + 2][0]
180 else:
181 # Add up to two balls from next frame
182 balls_added = 0
183 for ball in next_frame:
184 if balls_added < 2:
185 score += ball
186 balls_added += 1
187
188 return score
189
190 # Handled Edge Cases: Invalid number of pins, too many pins, game already completed, frame total exceeds 10, roll after game completion, game not complete, 10th frame incomplete, 10th frame is spare but missing fill ball
Test NameStatus
test_an_unstarted_game_cannot_be_scored
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_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.