unicode

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 or game is over.
17 """
18 # Check if game is already over
19 if self._is_game_over():
20 raise ValueError("game has already ended")
21
22 # Edge Case: Negative pins
23 if pins < 0:
24 raise ValueError("pins cannot be negative")
25
26 # Edge Case: Too many pins in a single roll
27 if pins > 10:
28 raise ValueError("invalid fill balls")
29
30 # For the tenth frame, we need special handling for fill balls
31 if len(self.rolls) >= 18: # We're in or past the tenth frame
32 # Check if this is a valid fill ball
33 if len(self.rolls) == 18:
34 # First roll of tenth frame - always valid
35 pass
36 elif len(self.rolls) == 19:
37 # Second roll of tenth frame
38 if self.rolls[18] == 10: # First roll was a strike
39 # Second roll can be anything 0-10
40 pass
41 else:
42 # Second roll of tenth frame after non-strike
43 if self.rolls[18] + pins > 10:
44 raise ValueError("cannot roll more than 10 pins in a frame")
45 elif len(self.rolls) == 20:
46 # This is a fill ball - need to check if it's valid
47 # Check what happened in the tenth frame
48 if self.rolls[18] == 10 and self.rolls[19] == 10:
49 # Two strikes in tenth frame - third roll is valid 0-10
50 pass
51 elif self.rolls[18] == 10 and self.rolls[19] < 10:
52 # Strike then non-strike - third roll must make valid frame
53 if self.rolls[19] + pins > 10:
54 raise ValueError("invalid fill balls")
55 elif self.rolls[18] + self.rolls[19] == 10:
56 # Spare in tenth frame - third roll is valid 0-10
57 pass
58 else:
59 # This shouldn't happen - game should be over
60 raise ValueError("game has already ended")
61 else:
62 # Regular frame (1-9)
63 # Need to determine if we're in the middle of a frame or starting a new one
64 # Count frames to determine current position
65 frame_count = 0
66 roll_index = 0
67
68 while frame_count < 10 and roll_index < len(self.rolls):
69 if self.rolls[roll_index] == 10: # Strike
70 roll_index += 1
71 frame_count += 1
72 else:
73 if roll_index + 1 < len(self.rolls):
74 roll_index += 2
75 frame_count += 1
76 else:
77 # We're in the middle of a frame
78 break
79
80 if roll_index < len(self.rolls) and frame_count < 10:
81 # We're in the middle of a frame (second roll)
82 if self.rolls[roll_index] == 10:
83 # This shouldn't happen - strikes are single rolls
84 raise ValueError("cannot roll more than 10 pins in a frame")
85 else:
86 # Check that frame total doesn't exceed 10
87 if self.rolls[roll_index] + pins > 10:
88 raise ValueError("cannot roll more than 10 pins in a frame")
89
90 self.rolls.append(pins)
91
892 def score(self):
9 pass
93 """
94 Calculate the total score for the game.
95
96 Returns:
97 int: The total score of the game.
98
99 Raises:
100 ValueError: If the game is not complete.
101 """
102 # Edge Case: Game not complete
103 if not self._is_game_over():
104 raise ValueError("game is not yet complete")
105
106 score = 0
107 roll_index = 0
108
109 for frame in range(10):
110 if roll_index >= len(self.rolls):
111 raise ValueError("game is not yet complete")
112
113 if self.rolls[roll_index] == 10: # Strike
114 # Strike gets bonus of next two rolls
115 if roll_index + 2 >= len(self.rolls):
116 raise ValueError("game is not yet complete")
117
118 score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
119 roll_index += 1
120 else:
121 # Open frame or spare
122 if roll_index + 1 >= len(self.rolls):
123 raise ValueError("game is not yet complete")
124
125 frame_score = self.rolls[roll_index] + self.rolls[roll_index + 1]
126
127 if frame_score == 10: # Spare
128 # Spare gets bonus of next roll
129 if roll_index + 2 >= len(self.rolls):
130 raise ValueError("game is not yet complete")
131 score += 10 + self.rolls[roll_index + 2]
132 else: # Open frame
133 score += frame_score
134 roll_index += 2
135
136 return score
137
138 def _is_game_over(self):
139 """
140 Check if the game is over.
141
142 Returns:
143 bool: True if the game is over, False otherwise.
144 """
145 # Count completed frames
146 frame_count = 0
147 roll_index = 0
148
149 # Count all completed frames (including 10th frame)
150 while frame_count < 10 and roll_index < len(self.rolls):
151 if roll_index < len(self.rolls) and self.rolls[roll_index] == 10: # Strike
152 # For 10th frame, check if we have the required bonus rolls
153 if frame_count == 9: # 10th frame
154 # Need two more rolls total
155 if roll_index + 2 < len(self.rolls):
156 frame_count += 1
157 roll_index += 3 # Move past all three rolls
158 else:
159 break
160 else: # Frames 1-9
161 roll_index += 1
162 frame_count += 1
163 else:
164 # Open frame or spare
165 if roll_index + 1 < len(self.rolls):
166 if frame_count == 9: # 10th frame
167 # Check if it's a spare
168 if self.rolls[roll_index] + self.rolls[roll_index + 1] == 10:
169 # Spare in 10th frame - need one more roll
170 if roll_index + 2 < len(self.rolls):
171 frame_count += 1
172 roll_index += 3
173 else:
174 break
175 else:
176 # Open frame in 10th frame - no bonus rolls needed
177 frame_count += 1
178 roll_index += 2
179 else: # Frames 1-9
180 frame_count += 1
181 roll_index += 2
182 else:
183 # Incomplete frame
184 break
185
186 # Game is over if we have completed 10 frames
187 return frame_count >= 10
Test NameStatus
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_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.