relax

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a 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 :param pins: Number of pins knocked down in this roll.
14 :raises ValueError: If the number of pins is invalid.
15 """
16 # Edge Case: Invalid number of pins (negative or more than 10)
17 if pins < 0 or pins > 10:
18 raise ValueError("Pins must be between 0 and 10")
19
20 # Edge Case: Too many pins knocked down in a frame
21 if self.current_roll > 0 and self.rolls[-1] + pins > 10 and self.current_roll % 2 == 1 and self.current_roll < 18:
22 raise ValueError("Invalid roll: pins downed in a frame cannot exceed 10")
23
24 # Edge Case: Game already has 10 complete frames
25 frame_index = self.current_roll // 2
26 if frame_index >= 10:
27 # For the 10th frame, we need special handling
28 # Edge Case: Too many rolls in the 10th frame
29 if self.current_roll >= 21:
30 raise ValueError("Cannot roll after game is over")
31
32 # Check if the current roll is valid in the 10th frame
33 if self.current_roll == 18: # First roll of 10th frame
34 self.rolls.append(pins)
35 self.current_roll += 1
36 elif self.current_roll == 19: # Second roll of 10th frame
37 # If first roll was a strike, second roll can be anything up to 10
38 if self.rolls[18] == 10:
39 self.rolls.append(pins)
40 self.current_roll += 1
41 # Otherwise, sum of first two rolls cannot exceed 10
42 elif self.rolls[18] + pins > 10:
43 raise ValueError("Invalid roll: pins downed in a frame cannot exceed 10")
44 else:
45 self.rolls.append(pins)
46 self.current_roll += 1
47 elif self.current_roll == 20: # Third roll of 10th frame
48 # This is only allowed if previous rolls were a strike or spare
49 # Edge Case: Invalid third roll in 10th frame
50 if not ((self.rolls[18] == 10) or (self.rolls[18] + self.rolls[19] == 10)):
51 raise ValueError("Invalid roll: cannot have a third roll in 10th frame without strike or spare")
52
53 # If second roll was a strike, third roll can be up to 10
54 # Otherwise, if first+second was a spare, third roll can be up to 10
55 # If first+second < 10, this shouldn't happen
56 self.rolls.append(pins)
57 self.current_roll += 1
58 else:
59 self.rolls.append(pins)
60 self.current_roll += 1
61
862 def score(self):
9 pass
63 """
64 Calculate the total score for the game.
65
66 :return: Total score of the game.
67 :raises ValueError: If the game is not complete.
68 """
69 # Edge Case: Game not complete
70 if not self._is_game_complete():
71 raise ValueError("Score cannot be taken until the end of the game")
72
73 total_score = 0
74 roll_index = 0
75
76 for frame in range(10):
77 # Edge Case: Strike in regular frames (1-9)
78 if frame < 9 and self._is_strike(roll_index):
79 total_score += 10 + self._strike_bonus(roll_index)
80 roll_index += 1
81 # Edge Case: Spare in regular frames (1-9)
82 elif frame < 9 and self._is_spare(roll_index):
83 total_score += 10 + self._spare_bonus(roll_index)
84 roll_index += 2
85 # Edge Case: Open frame in regular frames (1-9)
86 elif frame < 9:
87 total_score += self._sum_of_balls_in_frame(roll_index)
88 roll_index += 2
89 # Edge Case: 10th frame scoring
90 else: # 10th frame
91 total_score += self._sum_of_balls_in_frame_10(roll_index)
92 # For 10th frame, we don't advance roll_index in the same way
93 # since it can have 2 or 3 rolls
94 break
95
96 return total_score
97
98 def _is_game_complete(self):
99 """
100 Check if the game is complete.
101
102 :return: True if the game is complete, False otherwise.
103 """
104 # Game is complete when we have 10 frames
105 # For frames 1-9: 2 rolls each = 18 rolls
106 # For frame 10: 2 or 3 rolls depending on strike/spare
107
108 # Edge Case: Not enough rolls
109 if self.current_roll < 18:
110 return False
111
112 # Edge Case: Exactly 18 rolls (10 frames with no strike/spare in 10th frame)
113 if self.current_roll == 18:
114 return True
115
116 # Edge Case: 19 rolls (first roll of 10th frame was a strike)
117 if self.current_roll == 19:
118 # Need one more roll
119 return False
120
121 # Edge Case: 20 rolls (need to check if 10th frame was strike or spare)
122 if self.current_roll == 20:
123 # If 10th frame first roll was a strike or first two rolls were a spare
124 return (self.rolls[18] == 10) or (self.rolls[18] + self.rolls[19] == 10)
125
126 # Edge Case: 21 rolls (complete 10th frame with strike/spare)
127 if self.current_roll == 21:
128 return True
129
130 return False
131
132 def _is_strike(self, roll_index):
133 """
134 Check if the roll at roll_index is a strike.
135
136 :param roll_index: Index of the roll to check.
137 :return: True if it's a strike, False otherwise.
138 """
139 return self.rolls[roll_index] == 10
140
141 def _is_spare(self, roll_index):
142 """
143 Check if the frame starting at roll_index is a spare.
144
145 :param roll_index: Index of the first roll of the frame.
146 :return: True if it's a spare, False otherwise.
147 """
148 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
149
150 def _strike_bonus(self, roll_index):
151 """
152 Calculate the bonus for a strike.
153
154 :param roll_index: Index of the strike roll.
155 :return: Bonus points for the strike.
156 """
157 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
158
159 def _spare_bonus(self, roll_index):
160 """
161 Calculate the bonus for a spare.
162
163 :param roll_index: Index of the first roll of the spare frame.
164 :return: Bonus points for the spare.
165 """
166 return self.rolls[roll_index + 2]
167
168 def _sum_of_balls_in_frame(self, roll_index):
169 """
170 Calculate the sum of pins knocked down in a frame (for frames 1-9).
171
172 :param roll_index: Index of the first roll of the frame.
173 :return: Sum of pins in the frame.
174 """
175 return self.rolls[roll_index] + self.rolls[roll_index + 1]
176
177 def _sum_of_balls_in_frame_10(self, roll_index):
178 """
179 Calculate the sum of pins knocked down in the 10th frame.
180
181 :param roll_index: Index of the first roll of the 10th frame (should be 18).
182 :return: Sum of pins in the 10th frame.
183 """
184 # For the 10th frame, we sum all rolls in the frame
185 # This could be 2 or 3 rolls depending on strike/spare
186 if self.current_roll >= 21: # Three rolls in 10th frame
187 return self.rolls[18] + self.rolls[19] + self.rolls[20]
188 elif self.current_roll >= 20: # Two rolls in 10th frame
189 return self.rolls[18] + self.rolls[19]
190 else: # Should not happen if game is complete
191 return self.rolls[18] + self.rolls[19]
192
193# Handled Edge Cases: Invalid number of pins, too many pins in a frame, game already complete, too many rolls in 10th frame, invalid third roll in 10th frame, game not complete when scoring, strike in regular frames, spare in regular frames, open frame in regular frames, 10th frame scoring, not enough rolls, exactly 18 rolls, 19 rolls, 20 rolls, 21 rolls
Test NameStatus
test_a_spare_followed_by_zeros_is_worth_ten_points
Fail
test_a_strike_earns_ten_points_in_a_frame_with_a_single_roll
Fail
test_all_strikes_is_a_perfect_game
Fail
test_bonus_roll_for_a_spare_in_the_last_frame_must_be_rolled_before_score_can_be_calculated
Fail
test_both_bonus_rolls_for_a_strike_in_the_last_frame_must_be_rolled_before_score_can_be_calculated
Fail
test_consecutive_spares_each_get_a_one_roll_bonus
Fail
test_consecutive_strikes_each_get_the_two_roll_bonus
Fail
test_last_two_strikes_followed_by_only_last_bonus_with_non_strike_points
Fail
test_points_scored_in_the_roll_after_a_spare_are_counted_twice
Fail
test_points_scored_in_the_two_rolls_after_a_strike_are_counted_twice_as_a_bonus
Fail
test_should_be_able_to_score_a_game_with_all_zeros
Fail
test_should_be_able_to_score_a_game_with_no_strikes_or_spares
Fail
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_in_the_last_frame_gets_a_one_roll_bonus_that_is_counted_once
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_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_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_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_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.