bohdan

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: Negative pins
20 if pins < 0:
21 raise ValueError("Pins must be between 0 and 10")
22
23 # Edge Case: Too many pins in a single roll
24 if pins > 10:
25 raise ValueError("Pins must be between 0 and 10")
26
27 # Edge Case: Too many pins in a frame
28 # Only validate frame totals for frames 1-9
29 if self.current_roll > 0 and self.rolls[-1] != 10:
30 # Check if we're in frames 1-9 by counting completed frames
31 completed_frames = 0
32 i = 0
33 while i < len(self.rolls) and completed_frames < 9:
34 if self.rolls[i] == 10: # Strike
35 completed_frames += 1
36 i += 1
37 else:
38 if i + 1 < len(self.rolls): # Make sure we have two rolls
39 completed_frames += 1
40 i += 2
41 else:
42 break
43
44 # Only apply frame validation if we are in frames 1-9
45 if completed_frames < 9 or (completed_frames == 9 and len(self.rolls) % 2 == 1):
46 if self.rolls[-1] + pins > 10:
47 raise ValueError("Cannot knock down more than 10 pins in a frame")
48
49 # Edge Case: Game already completed
50 if self.is_game_complete():
51 raise ValueError("Game has already ended")
52
53 self.rolls.append(pins)
54 self.current_roll += 1
55
856 def score(self):
9 pass
57 """
58 Calculate the total score for the game.
59
60 Returns:
61 int: The total score for the game.
62
63 Raises:
64 ValueError: If the game is not yet complete.
65 """
66 # Edge Case: Game not yet complete
67 if not self.is_game_complete():
68 raise ValueError("Game is not yet complete")
69
70 total_score = 0
71 roll_index = 0
72
73 for frame in range(10):
74 # Edge Case: Strike in regular frames (1-9)
75 if self.is_strike(roll_index) and frame < 9:
76 total_score += 10 + self.strike_bonus(roll_index)
77 roll_index += 1
78 # Edge Case: Spare in regular frames (1-9)
79 elif self.is_spare(roll_index) and frame < 9:
80 total_score += 10 + self.spare_bonus(roll_index)
81 roll_index += 2
82 # Edge Case: Open frame in regular frames (1-9)
83 elif frame < 9:
84 total_score += self.sum_of_balls_in_frame(roll_index)
85 roll_index += 2
86 # Edge Case: Tenth frame handling
87 else: # 10th frame
88 # Edge Case: Strike in tenth frame
89 if self.is_strike(roll_index):
90 # Edge Case: Invalid fill balls for strike in tenth frame
91 if len(self.rolls) < roll_index + 3:
92 raise ValueError("invalid fill balls")
93 # Edge Case: Too many pins in fill balls
94 if self.rolls[roll_index+1] != 10 and self.rolls[roll_index+1] + self.rolls[roll_index+2] > 10 and self.rolls[roll_index+1] != 10:
95 raise ValueError("invalid fill balls")
96 total_score += 10 + self.rolls[roll_index+1] + self.rolls[roll_index+2]
97 # Edge Case: Spare in tenth frame
98 elif self.is_spare(roll_index):
99 # Edge Case: Invalid fill balls for spare in tenth frame
100 if len(self.rolls) < roll_index + 3:
101 raise ValueError("invalid fill balls")
102 total_score += 10 + self.rolls[roll_index+2]
103 # Edge Case: Open tenth frame
104 else:
105 # Edge Case: Too many rolls in tenth frame
106 if len(self.rolls) > roll_index + 2:
107 raise ValueError("invalid fill balls")
108 total_score += self.sum_of_balls_in_frame(roll_index)
109
110 return total_score
111
112 def is_game_complete(self):
113 """
114 Check if the game is complete.
115
116 Returns:
117 bool: True if the game is complete, False otherwise.
118 """
119 roll_index = 0
120
121 for frame in range(10):
122 # Check if we have enough rolls for this frame
123 if roll_index >= len(self.rolls):
124 return False
125
126 if frame < 9: # Frames 1-9
127 if roll_index < len(self.rolls) and self.rolls[roll_index] == 10:
128 roll_index += 1
129 else:
130 # Need 2 rolls for non-strike frames
131 if roll_index + 1 >= len(self.rolls):
132 return False
133 roll_index += 2
134 else: # 10th frame
135 if roll_index >= len(self.rolls):
136 return False
137
138 if self.rolls[roll_index] == 10:
139 # Strike in 10th frame needs 2 more rolls
140 return len(self.rolls) >= roll_index + 3
141 elif roll_index + 1 < len(self.rolls) and self.rolls[roll_index] + self.rolls[roll_index + 1] == 10:
142 # Spare in 10th frame needs 1 more roll
143 return len(self.rolls) >= roll_index + 3
144 else:
145 # Open frame in 10th frame needs no more rolls
146 return len(self.rolls) >= roll_index + 2
147
148 return len(self.rolls) >= roll_index
149
150 def is_strike(self, roll_index):
151 """
152 Check if a roll is a strike.
153
154 Args:
155 roll_index (int): Index of the roll to check.
156
157 Returns:
158 bool: True if the roll is a strike, False otherwise.
159 """
160 if roll_index >= len(self.rolls):
161 return False
162 return self.rolls[roll_index] == 10
163
164 def is_spare(self, roll_index):
165 """
166 Check if a frame is a spare.
167
168 Args:
169 roll_index (int): Index of the first roll in the frame.
170
171 Returns:
172 bool: True if the frame is a spare, False otherwise.
173 """
174 if roll_index + 1 >= len(self.rolls):
175 return False
176 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
177
178 def strike_bonus(self, roll_index):
179 """
180 Calculate the bonus for a strike.
181
182 Args:
183 roll_index (int): Index of the strike roll.
184
185 Returns:
186 int: The bonus points for the strike.
187 """
188 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
189
190 def spare_bonus(self, roll_index):
191 """
192 Calculate the bonus for a spare.
193
194 Args:
195 roll_index (int): Index of the first roll in the spare frame.
196
197 Returns:
198 int: The bonus points for the spare.
199 """
200 return self.rolls[roll_index + 2]
201
202 def sum_of_balls_in_frame(self, roll_index):
203 """
204 Calculate the sum of pins knocked down in a frame.
205
206 Args:
207 roll_index (int): Index of the first roll in the frame.
208
209 Returns:
210 int: The sum of pins knocked down in the frame.
211 """
212 return self.rolls[roll_index] + self.rolls[roll_index + 1]
213
214 # Handled Edge Cases: Negative pins, too many pins in a single roll, too many pins in a frame, game already completed, game not yet complete, strike in regular frames, spare in regular frames, open frame in regular frames, tenth frame handling, strike in tenth frame, invalid fill balls for strike in tenth frame, too many pins in fill balls, spare in tenth frame, invalid fill balls for spare in tenth frame, open tenth frame, too many rolls in tenth frame
Test NameStatus
test_consecutive_spares_each_get_a_one_roll_bonus
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_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_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.