uv

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5 """
6 self.rolls = []
7 self.current_frame = 1
8 self.frame_rolls = []
9 self.frames = []
10
511 def roll(self, pins):
6 pass
7
12 """
13 Record a roll in the game.
14
15 Args:
16 pins (int): Number of pins knocked down in this roll
17
18 Raises:
19 ValueError: If pins is negative or greater than 10
20 IndexError: If game is already over
21 """
22 # Edge Case: Invalid number of pins
23 if pins < 0:
24 raise ValueError("Pins must be between 0 and 10")
25
26 # Edge Case: Too many pins
27 if pins > 10:
28 raise ValueError("Pins must be between 0 and 10")
29
30 # Edge Case: Game already finished
31 if len(self.frames) == 10 and len(self.frame_rolls) == 0:
32 raise IndexError("Game has already ended")
33
34 # Edge Case: Tenth frame special rules
35 if self.current_frame == 10:
36 self._handle_tenth_frame(pins)
37 return
38
39 self.frame_rolls.append(pins)
40 self.rolls.append(pins)
41
42 # Edge Case: Strike in regular frame
43 if len(self.frame_rolls) == 1 and pins == 10:
44 self.frames.append(self.frame_rolls[:])
45 self.frame_rolls = []
46 self.current_frame += 1
47 # Edge Case: Two rolls in frame
48 elif len(self.frame_rolls) == 2:
49 # Edge Case: Invalid frame total
50 if sum(self.frame_rolls) > 10 and self.frame_rolls[0] != 10:
51 raise ValueError("Cannot knock down more pins than remaining")
52 self.frames.append(self.frame_rolls[:])
53 self.frame_rolls = []
54 self.current_frame += 1
55
56 def _handle_tenth_frame(self, pins):
57 """
58 Handle special rules for the tenth frame.
59
60 Args:
61 pins (int): Number of pins knocked down
62 """
63 self.frame_rolls.append(pins)
64 self.rolls.append(pins)
65
66 # Edge Case: First roll is a strike
67 if len(self.frame_rolls) == 1 and pins == 10:
68 return
69
70 # Edge Case: Second roll after strike
71 if len(self.frame_rolls) == 2 and self.frame_rolls[0] == 10:
72 # Edge Case: Invalid second roll
73 if pins > 10:
74 raise ValueError("Pins must be between 0 and 10")
75 return
76
77 # Edge Case: Second roll after non-strike
78 if len(self.frame_rolls) == 2 and self.frame_rolls[0] != 10:
79 # Edge Case: Invalid frame total
80 if sum(self.frame_rolls) > 10:
81 raise ValueError("Cannot knock down more pins than remaining")
82 # Edge Case: Spare in tenth frame - need one more roll
83 if sum(self.frame_rolls) == 10:
84 return
85 # Edge Case: Open frame - game over
86 else:
87 self.frames.append(self.frame_rolls[:])
88 self.frame_rolls = []
89 return
90
91 # Edge Case: Third roll
92 if len(self.frame_rolls) == 3:
93 # Edge Case: Invalid third roll for strike scenario
94 if self.frame_rolls[0] == 10:
95 if self.frame_rolls[1] == 10: # Two strikes, third roll can be anything up to 10
96 if self.frame_rolls[2] > 10:
97 raise ValueError("Pins must be between 0 and 10")
98 elif (self.frame_rolls[1] + self.frame_rolls[2]) > 10:
99 raise ValueError("Cannot knock down more pins than remaining")
100 # Edge Case: Invalid third roll for spare scenario
101 elif (self.frame_rolls[0] + self.frame_rolls[1]) == 10:
102 if self.frame_rolls[2] > 10:
103 raise ValueError("Pins must be between 0 and 10")
104 # Edge Case: Should not have a third roll for open frame
105 else:
106 raise IndexError("Game has already ended")
107 self.frames.append(self.frame_rolls[:])
108 self.frame_rolls = []
109
8110 def score(self):
9 pass
111 """
112 Calculate the total score for the game.
113
114 Returns:
115 int: Total score for the game
116
117 Raises:
118 IndexError: If game is not yet complete
119 """
120 # Edge Case: Game not complete
121 if len(self.frames) < 10:
122 raise IndexError("Game is not yet complete")
123
124 # Edge Case: Tenth frame incomplete
125 if len(self.frames) == 10 and len(self.frame_rolls) > 0:
126 raise IndexError("Game is not yet complete")
127
128 total = 0
129 roll_index = 0
130
131 for frame_num in range(10):
132 frame = self.frames[frame_num]
133
134 # Edge Case: Regular frame
135 if frame_num < 9:
136 if len(frame) == 1: # Strike
137 total += 10 + self._next_two_balls(roll_index)
138 roll_index += 1
139 elif len(frame) == 2:
140 frame_score = sum(frame)
141 if frame_score == 10: # Spare
142 total += 10 + self._next_ball(roll_index + 2)
143 else: # Open frame
144 total += frame_score
145 roll_index += 2
146 else: # Tenth frame
147 total += sum(frame)
148
149 return total
150
151 def _next_ball(self, roll_index):
152 """
153 Get the value of the next ball.
154
155 Args:
156 roll_index (int): Index of the current roll
157
158 Returns:
159 int: Pins knocked down in the next ball
160 """
161 if roll_index < len(self.rolls):
162 return self.rolls[roll_index]
163 return 0
164
165 def _next_two_balls(self, roll_index):
166 """
167 Get the value of the next two balls.
168
169 Args:
170 roll_index (int): Index of the current roll
171
172 Returns:
173 int: Pins knocked down in the next two balls
174 """
175 result = 0
176 if roll_index + 1 < len(self.rolls):
177 result += self.rolls[roll_index + 1]
178 if roll_index + 2 < len(self.rolls):
179 result += self.rolls[roll_index + 2]
180 return result
181
182 # Handled Edge Cases: Invalid number of pins, too many pins, game already finished,
183 # strike in regular frame, two rolls in frame, invalid frame total, first roll is a strike,
184 # second roll after strike, second roll after non-strike, invalid frame total,
185 # third roll, invalid third roll, game not complete, tenth frame incomplete, regular frame
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.