sn62

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
59 def roll(self, pins):
6 pass
7
10 """
11 Record a roll in the game.
12
13 Args:
14 pins (int): Number of pins knocked down in this roll.
15
16 Raises:
17 ValueError: If the number of pins is invalid.
18 """
19 # Edge Case: Invalid number of pins (negative)
20 if pins < 0:
21 raise ValueError("Pins cannot be negative")
22
23 # Edge Case: Invalid number of pins (more than 10)
24 if pins > 10:
25 raise ValueError("Pins cannot be more than 10")
26
27 # Edge Case: Game already has 10 frames and no more rolls are allowed
28 if self._is_game_complete():
29 raise ValueError("Game is already finished")
30
31 # Edge Case: Invalid roll in frame (trying to knock down more pins than remaining)
32 if self.current_roll % 2 == 1: # Second roll in a frame
33 # Check if this is a valid second roll (not exceeding 10 pins in frame)
34 if self.current_roll > 0:
35 first_roll_in_frame = self.rolls[self.current_roll - 1]
36 if first_roll_in_frame != 10 and first_roll_in_frame + pins > 10:
37 raise ValueError("Frame cannot have more than 10 pins")
38
39 self.rolls.append(pins)
40 self.current_roll += 1
41
842 def score(self):
9 pass
43 """
44 Calculate the total score for the game.
45
46 Returns:
47 int: The total score of the game.
48
49 Raises:
50 ValueError: If the game is not yet complete.
51 """
52 # Edge Case: Game is not complete
53 if not self._is_game_complete():
54 raise ValueError("Score cannot be taken until the end of the game")
55
56 total_score = 0
57 roll_index = 0
58
59 for frame in range(10):
60 # Edge Case: Strike in frames 1-9
61 if self._is_strike(roll_index) and frame < 9:
62 total_score += 10 + self._strike_bonus(roll_index)
63 roll_index += 1
64 # Edge Case: Spare in frames 1-9
65 elif self._is_spare(roll_index) and frame < 9:
66 total_score += 10 + self._spare_bonus(roll_index)
67 roll_index += 2
68 # Edge Case: Open frame in frames 1-9
69 elif frame < 9:
70 total_score += self.rolls[roll_index] + self.rolls[roll_index + 1]
71 roll_index += 2
72 # Edge Case: Tenth frame scoring
73 else: # frame == 9 (10th frame)
74 if self._is_strike(roll_index):
75 # Strike in 10th frame
76 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
77 elif self._is_spare(roll_index):
78 # Spare in 10th frame
79 total_score += 10 + self.rolls[roll_index + 2]
80 else:
81 # Open 10th frame
82 total_score += self.rolls[roll_index] + self.rolls[roll_index + 1]
83
84 return total_score
85
86 def _is_strike(self, roll_index):
87 """
88 Check if a roll is a strike.
89
90 Args:
91 roll_index (int): Index of the roll to check.
92
93 Returns:
94 bool: True if the roll is a strike, False otherwise.
95 """
96 return self.rolls[roll_index] == 10
97
98 def _is_spare(self, roll_index):
99 """
100 Check if a frame is a spare.
101
102 Args:
103 roll_index (int): Index of the first roll in the frame.
104
105 Returns:
106 bool: True if the frame is a spare, False otherwise.
107 """
108 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
109
110 def _strike_bonus(self, roll_index):
111 """
112 Calculate the bonus for a strike.
113
114 Args:
115 roll_index (int): Index of the strike roll.
116
117 Returns:
118 int: The bonus points for the strike.
119 """
120 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
121
122 def _spare_bonus(self, roll_index):
123 """
124 Calculate the bonus for a spare.
125
126 Args:
127 roll_index (int): Index of the first roll in the spare frame.
128
129 Returns:
130 int: The bonus points for the spare.
131 """
132 return self.rolls[roll_index + 2]
133
134 def _is_game_complete(self):
135 """
136 Check if the game is complete.
137
138 Returns:
139 bool: True if the game is complete, False otherwise.
140 """
141 # Count frames
142 frame_count = 0
143 roll_index = 0
144
145 # Count the first 9 frames
146 while frame_count < 9 and roll_index < len(self.rolls):
147 if self._is_strike(roll_index):
148 roll_index += 1
149 else:
150 roll_index += 2
151 frame_count += 1
152
153 # If we haven't completed 9 frames, game is not complete
154 if frame_count < 9:
155 return False
156
157 # Handle 10th frame
158 if frame_count == 9 and roll_index < len(self.rolls):
159 # If 10th frame is a strike
160 if self._is_strike(roll_index):
161 # Need two more rolls (fill balls)
162 return roll_index + 3 <= len(self.rolls)
163 # If 10th frame is a spare
164 elif roll_index + 1 < len(self.rolls) and self.rolls[roll_index] + self.rolls[roll_index + 1] == 10:
165 # Need one more roll (fill ball)
166 return roll_index + 3 <= len(self.rolls)
167 # If 10th frame is open
168 else:
169 return roll_index + 2 <= len(self.rolls)
170
171 return False
172
173 def _is_tenth_frame_open(self):
174 """
175 Check if the tenth frame is still open for more rolls.
176
177 Returns:
178 bool: True if the tenth frame is open, False otherwise.
179 """
180 # Count the first 9 frames
181 frame_count = 0
182 roll_index = 0
183
184 while frame_count < 9 and roll_index < len(self.rolls):
185 if self._is_strike(roll_index):
186 roll_index += 1
187 else:
188 roll_index += 2
189 frame_count += 1
190
191 # If we haven't completed 9 frames, the 10th is not yet open
192 if frame_count < 9:
193 return False
194
195 # Check the 10th frame
196 if frame_count == 9 and roll_index < len(self.rolls):
197 # If 10th frame is a strike
198 if self._is_strike(roll_index):
199 # Strike needs two fill balls
200 return roll_index + 3 > len(self.rolls)
201 # If 10th frame is a spare
202 elif roll_index + 1 < len(self.rolls) and self.rolls[roll_index] + self.rolls[roll_index + 1] == 10:
203 # Spare needs one fill ball
204 return roll_index + 3 > len(self.rolls)
205 # If 10th frame is open
206 else:
207 # Open frame needs no fill balls
208 return roll_index + 2 > len(self.rolls)
209
210 return False
211
212 # Handled Edge Cases: Invalid number of pins (negative), Invalid number of pins (more than 10),
213 # Game already has 10 frames and no more rolls are allowed, Invalid roll in frame (trying to knock down more pins than remaining),
214 # Game is not complete, Not enough rolls, Standard game (10 frames, 2 rolls each),
215 # Game with spares or strikes that needs fill balls, Strike in frames 1-9, Spare in frames 1-9,
216 # Open frame in frames 1-9, Tenth frame scoring
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.