Merry

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 the number of pins is invalid
17 StopIteration: If the game is already over
18 """
19 # Edge Case: Game is already over
20 if self.is_game_over():
21 raise StopIteration("game is already over")
22
23 # Edge Case: Invalid number of pins (negative)
24 if pins < 0:
25 raise ValueError("Pins must be between 0 and 10")
26
27 # Edge Case: Invalid number of pins (more than 10)
28 if pins > 10:
29 raise ValueError("Pins must be between 0 and 10")
30
31 # Edge Case: In regular frames, cannot exceed 10 pins in two rolls
32 # But we need to be careful about strikes and frame boundaries
33 if len(self.rolls) >= 18: # We might be in the 10th frame or bonus rolls
34 # For the 10th frame and bonus rolls, we need more complex validation
35 pass # We'll handle this with game completion logic
36 elif len(self.rolls) >= 1: # Not the first roll
37 # Simple check: if last roll was not a strike, we might be in the same frame
38 # Count frames to see if we're still in the first 9 frames
39 if self.rolls[-1] != 10: # Last roll was not a strike
40 # Count complete frames to see if we're in frames 1-9
41 frame_count = 0
42 roll_index = 0
43
44 while roll_index < len(self.rolls) and frame_count < 9:
45 if self.rolls[roll_index] == 10: # Strike
46 frame_count += 1
47 roll_index += 1
48 else:
49 if roll_index + 1 < len(self.rolls):
50 frame_count += 1
51 roll_index += 2
52 else:
53 # We're in the middle of a frame in the first 9 frames
54 # This is the second roll of that frame
55 if self.rolls[-1] + pins > 10:
56 raise ValueError("Cannot knock down more pins than remaining in frame")
57 break
58
59 self.rolls.append(pins)
60
61 def current_frame(self):
62 """Return the current frame number (1-10)."""
63 # Count complete frames in first 9 frames
64 frame_count = 0
65 roll_index = 0
66
67 while roll_index < len(self.rolls) and frame_count < 9:
68 if self.rolls[roll_index] == 10: # Strike
69 frame_count += 1
70 roll_index += 1
71 else:
72 if roll_index + 1 < len(self.rolls):
73 frame_count += 1
74 roll_index += 2
75 else:
76 break
77
78 # If we've completed 9 frames, we're in the 10th frame
79 if frame_count == 9:
80 return 10
81 else:
82 return frame_count + 1
83
84 def is_game_over(self):
85 """Check if the game is over."""
86 # Count frames and determine if we have a complete game
87 frame_count = 0
88 roll_index = 0
89
90 # Count the first 9 frames
91 while roll_index < len(self.rolls) and frame_count < 9:
92 if self.rolls[roll_index] == 10: # Strike
93 frame_count += 1
94 roll_index += 1
95 else:
96 if roll_index + 1 < len(self.rolls):
97 frame_count += 1
98 roll_index += 2
99 else:
100 # Incomplete frame
101 break
102
103 # Handle 10th frame
104 if frame_count == 9 and roll_index < len(self.rolls):
105 # We're in the 10th frame
106 if roll_index + 1 < len(self.rolls):
107 # We have at least two rolls in 10th frame
108 if self.rolls[roll_index] == 10: # 10th frame strike
109 # Need two more rolls for bonus
110 if roll_index + 2 < len(self.rolls):
111 return True # Game complete with strike and two bonus rolls
112 elif self.rolls[roll_index] + self.rolls[roll_index + 1] == 10: # 10th frame spare
113 # Need one more roll for bonus
114 if roll_index + 2 < len(self.rolls):
115 return True # Game complete with spare and one bonus roll
116 else: # 10th frame open
117 return True # Game complete with open frame
118 # Special case: if we have exactly 20 rolls and they form 10 complete frames
119 elif len(self.rolls) == 20:
120 # Check if the last frame is complete
121 if roll_index + 1 == len(self.rolls):
122 # Last roll is the second roll of 10th frame
123 return True
124
125 # Special case: exactly 20 rolls with all open frames
126 if len(self.rolls) == 20:
127 # Check if we have 10 complete open frames
128 temp_frame_count = 0
129 temp_roll_index = 0
130 while temp_roll_index < 20 and temp_frame_count < 10:
131 if temp_roll_index + 1 < 20:
132 if self.rolls[temp_roll_index] + self.rolls[temp_roll_index + 1] < 10:
133 temp_frame_count += 1
134 temp_roll_index += 2
135 else:
136 # Last frame is a strike or spare, need to check completion above
137 break
138 else:
139 break
140 if temp_frame_count == 10:
141 return True
142
143 return False
144
8145 def score(self):
9 pass
146 """
147 Calculate the total score for the game.
148
149 Returns:
150 int: The total score of the game
151
152 Raises:
153 ValueError: If the game is not yet complete
154 """
155 if not self.is_game_over():
156 raise ValueError("game is not yet complete")
157
158 total_score = 0
159 roll_index = 0
160
161 # Score all frames (including 10th frame with bonus balls)
162 frame_count = 0
163 while frame_count < 10 and roll_index < len(self.rolls):
164 # Strike (first 9 frames)
165 if frame_count < 9 and self.rolls[roll_index] == 10:
166 # Add strike bonus (next two rolls)
167 if roll_index + 2 < len(self.rolls):
168 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
169 else:
170 total_score += 10
171 roll_index += 1
172 # Spare or open frame (first 9 frames)
173 elif frame_count < 9 and roll_index + 1 < len(self.rolls):
174 frame_score = self.rolls[roll_index] + self.rolls[roll_index + 1]
175 # Spare
176 if frame_score == 10 and roll_index + 2 < len(self.rolls):
177 total_score += 10 + self.rolls[roll_index + 2]
178 # Open frame
179 else:
180 total_score += frame_score
181 roll_index += 2
182 # 10th frame
183 else:
184 # Score the 10th frame (including bonus balls)
185 remaining_rolls = len(self.rolls) - roll_index
186 if remaining_rolls >= 1:
187 total_score += self.rolls[roll_index]
188 if remaining_rolls >= 2:
189 total_score += self.rolls[roll_index + 1]
190 if remaining_rolls >= 3:
191 total_score += self.rolls[roll_index + 2]
192 break
193
194 frame_count += 1
195
196 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.