Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5 """
6 self.rolls = []
7
58 def roll(self, pins):
6 pass
7
9 """
10 Record a roll in the game.
11
12 Args:
13 pins (int): Number of pins knocked down in this roll
14
15 Raises:
16 ValueError: If pins is negative or greater than 10
17 IndexError: If game is already over
18 """
19 # Edge Case: Invalid number of pins
20 if pins < 0:
21 raise ValueError("Pins cannot be negative")
22
23 # Edge Case: Too many pins
24 if pins > 10:
25 raise ValueError("Pins cannot be more than 10")
26
27 # Edge Case: Game already completed
28 if self._is_game_complete():
29 raise IndexError("Game is already complete")
30
31 # Add the roll
32 self.rolls.append(pins)
33
34 # Validate frame rules after adding the roll
35 if len(self.rolls) >= 2:
36 # Check if current frame is valid
37 if not self._is_valid_frame_state():
38 # Remove the invalid roll and raise error
39 self.rolls.pop()
40 raise ValueError("Frame cannot have more than 10 pins")
41
42 def _is_valid_frame_state(self):
43 """
44 Check if the current frame state is valid.
45
46 Returns:
47 bool: True if the frame state is valid, False otherwise
48 """
49 # Count completed frames
50 roll_index = 0
51 frame_count = 0
52
53 # Process first 9 frames
54 while frame_count < 9 and roll_index < len(self.rolls):
55 if self.rolls[roll_index] == 10: # Strike
56 roll_index += 1
57 else:
58 if roll_index + 1 < len(self.rolls):
59 # Check if frame is valid
60 if self.rolls[roll_index] + self.rolls[roll_index + 1] > 10:
61 return False
62 roll_index += 2
63 else:
64 # Incomplete frame, check if first roll is valid
65 if self.rolls[roll_index] > 10:
66 return False
67 # Valid incomplete frame
68 break
69 frame_count += 1
70
71 # If we have processed all first 9 frames, check 10th frame
72 if frame_count == 9:
73 remaining_rolls = len(self.rolls) - roll_index
74 if remaining_rolls >= 1:
75 first_roll = self.rolls[roll_index]
76 if first_roll > 10:
77 return False
78 if remaining_rolls >= 2:
79 second_roll = self.rolls[roll_index + 1]
80 if first_roll != 10 and first_roll + second_roll > 10:
81 return False
82
83 return True
84
85 def _is_game_complete(self):
86 """
87 Check if the game is complete.
88
89 Returns:
90 bool: True if the game is complete, False otherwise
91 """
92 # Count frames
93 roll_index = 0
94 frame_count = 0
95
96 # Process first 9 frames
97 while frame_count < 9 and roll_index < len(self.rolls):
98 if self.rolls[roll_index] == 10: # Strike
99 roll_index += 1
100 else:
101 if roll_index + 1 < len(self.rolls):
102 roll_index += 2
103 else:
104 # Not enough rolls to complete frame
105 return False
106 frame_count += 1
107
108 # Process 10th frame
109 if frame_count == 9:
110 remaining_rolls = len(self.rolls) - roll_index
111 if remaining_rolls >= 2:
112 # Check if we have a valid 10th frame
113 first_roll = self.rolls[roll_index]
114 second_roll = self.rolls[roll_index + 1] if roll_index + 1 < len(self.rolls) else None
115 third_roll = self.rolls[roll_index + 2] if roll_index + 2 < len(self.rolls) else None
116
117 if first_roll == 10: # Strike in 10th frame
118 # Need exactly 3 rolls total for 10th frame
119 return remaining_rolls == 3
120 elif second_roll is not None and first_roll + second_roll == 10: # Spare in 10th frame
121 # Need exactly 3 rolls total for 10th frame
122 return remaining_rolls == 3
123 elif second_roll is not None and first_roll + second_roll < 10: # Open frame in 10th frame
124 # Need exactly 2 rolls total for 10th frame
125 return remaining_rolls == 2
126 else:
127 return False
128 else:
129 return False
130
131 # If we haven't processed 9 frames yet, game is not complete
132 return False
133
8134 def score(self):
9 pass
135 """
136 Calculate the total score for the game.
137
138 Returns:
139 int: The total score of the game
140
141 Raises:
142 IndexError: If the game is not yet complete
143 """
144 # Edge Case: Game not complete
145 if not self._is_game_complete():
146 raise IndexError("Game is not yet complete")
147
148 total_score = 0
149 roll_index = 0
150
151 # Score first 9 frames
152 for frame_num in range(9):
153 if roll_index >= len(self.rolls):
154 raise IndexError("Game is not yet complete")
155
156 # Edge Case: Strike
157 if self.rolls[roll_index] == 10: # Strike
158 if roll_index + 2 < len(self.rolls):
159 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
160 else:
161 raise IndexError("Game is not yet complete")
162 roll_index += 1
163 else:
164 if roll_index + 1 < len(self.rolls):
165 frame_score = self.rolls[roll_index] + self.rolls[roll_index + 1]
166 # Edge Case: Spare
167 if frame_score == 10: # Spare
168 if roll_index + 2 < len(self.rolls):
169 total_score += 10 + self.rolls[roll_index + 2]
170 else:
171 raise IndexError("Game is not yet complete")
172 else: # Open frame
173 total_score += frame_score
174 roll_index += 2
175 else:
176 raise IndexError("Game is not yet complete")
177
178 # Score 10th frame
179 # Add all remaining rolls in the 10th frame
180 while roll_index < len(self.rolls):
181 total_score += self.rolls[roll_index]
182 roll_index += 1
183
184 return total_score
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.