viola

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5
6 Sets up the game state including frames, current frame index,
7 and whether we're in the 10th frame special case.
8 """
9 self.frames = [[] for _ in range(10)]
10 self.current_frame = 0
11 self.game_over = False
12
513 def roll(self, pins):
6 pass
14 """
15 Record a roll in the current frame.
16
17 Args:
18 pins (int): Number of pins knocked down in this roll
19
20 Raises:
21 ValueError: If pins is negative or greater than 10
22 IndexError: If attempting to roll after game is over
23 """
24 # Edge Case: Invalid pin count (negative or more than 10)
25 if pins < 0 or pins > 10:
26 raise ValueError("Pins must be between 0 and 10")
27
28 # Edge Case: Attempting to roll after game is over
29 if self.game_over:
30 raise ValueError("Game has ended")
31
32 # Edge Case: Trying to roll in a frame that's already complete
33 if len(self.frames[self.current_frame]) == 2 and self.current_frame < 9:
34 self.current_frame += 1
35 elif len(self.frames[self.current_frame]) == 3 and self.current_frame == 9:
36 self.game_over = True
37 raise IndexError("Cannot roll after game is over")
38
39 # Edge Case: In 10th frame, invalid roll after strike or spare
40 if self.current_frame == 9:
41 frame_rolls = self.frames[9]
42 if len(frame_rolls) == 2:
43 # If first two rolls are a strike or spare, we need a third roll
44 if frame_rolls[0] == 10 or sum(frame_rolls) == 10:
45 # Valid - proceed to add the roll
46 pass
47 else:
48 # Not a strike or spare, so game is over
49 self.game_over = True
50 raise IndexError("Cannot roll after game is over")
51 elif len(frame_rolls) == 3:
52 # Game is definitely over
53 self.game_over = True
54 raise IndexError("Cannot roll after game is over")
55
56 # Edge Case: In regular frames, can't roll if previous frame was open and current frame is full
57 if self.current_frame < 9 and len(self.frames[self.current_frame]) == 2:
58 # Move to next frame
59 self.current_frame += 1
60
61 # Edge Case: In regular frames, invalid pin count based on previous roll
62 if self.current_frame < 9 and len(self.frames[self.current_frame]) == 1:
63 if self.frames[self.current_frame][0] + pins > 10:
64 raise ValueError("Cannot knock down more than 10 pins in a frame")
65
66 # Edge Case: In 10th frame, invalid pin count based on previous rolls
67 if self.current_frame == 9:
68 frame_rolls = self.frames[9]
69 if len(frame_rolls) == 1 and frame_rolls[0] != 10: # First roll wasn't a strike
70 if frame_rolls[0] + pins > 10:
71 raise ValueError("Cannot knock down more than 10 pins in a frame")
72 elif len(frame_rolls) == 2: # Two rolls already recorded
73 # If first was a strike
74 if frame_rolls[0] == 10:
75 # Second roll can be anything 0-10
76 # But third roll has constraints based on second
77 if frame_rolls[1] != 10 and frame_rolls[1] + pins > 10:
78 raise ValueError("Cannot knock down more than 10 pins in a frame")
79 # If first two make a spare
80 elif sum(frame_rolls) == 10:
81 # Third roll can be 0-10
82 pass
83 # If first two are an open frame
84 else:
85 # Should not be a third roll
86 raise IndexError("Cannot roll after game is over")
87
88 # Add the roll to the current frame
89 self.frames[self.current_frame].append(pins)
90
91 # Check if we need to move to next frame (for frames 1-9)
92 if self.current_frame < 9:
93 # If it's a strike or the frame is complete
94 if pins == 10 or len(self.frames[self.current_frame]) == 2:
95 self.current_frame += 1
96 # For the 10th frame, check if game is over
97 elif self.current_frame == 9:
98 frame_rolls = self.frames[9]
99 # If we have 2 rolls and it's not a strike/spare, game over
100 if len(frame_rolls) == 2 and frame_rolls[0] != 10 and sum(frame_rolls) < 10:
101 self.game_over = True
102 # If we have 3 rolls, game over
103 elif len(frame_rolls) == 3:
104 self.game_over = True
7105
8106 def score(self):
9 pass
107 """
108 Calculate the total score for the game.
109
110 Returns:
111 int: The total score of the game
112
113 Raises:
114 ValueError: If the game is not yet complete
115 """
116 # Edge Case: Attempting to score before game is complete
117 if not self.game_over:
118 # Check if no rolls have been made (new game)
119 if all(len(frame) == 0 for frame in self.frames):
120 return 0
121 # Check if 10th frame is complete
122 tenth_frame = self.frames[9]
123 if len(tenth_frame) < 2:
124 raise ValueError("Game is not yet complete")
125 if len(tenth_frame) == 2 and (tenth_frame[0] == 10 or sum(tenth_frame) == 10):
126 raise ValueError("Game is not yet complete")
127
128 total_score = 0
129
130 # Score frames 1-9
131 for i in range(9):
132 frame = self.frames[i]
133 frame_score = sum(frame)
134
135 # Edge Case: Incomplete frame
136 if len(frame) == 0:
137 continue
138 elif len(frame) == 1 and frame[0] != 10:
139 # This shouldn't happen in a valid game state
140 continue
141
142 # Strike
143 if len(frame) == 1 and frame[0] == 10:
144 # Add next two rolls as bonus
145 next_frame_index = i + 1
146 bonus_rolls = 0
147
148 # Get next rolls
149 while next_frame_index < 10 and bonus_rolls < 2:
150 next_frame = self.frames[next_frame_index]
151 for roll_val in next_frame:
152 frame_score += roll_val
153 bonus_rolls += 1
154 if bonus_rolls == 2:
155 break
156 next_frame_index += 1
157
158 # Spare
159 elif len(frame) == 2 and sum(frame) == 10:
160 # Add next roll as bonus
161 next_frame_index = i + 1
162 bonus_added = False
163
164 # Get next roll
165 while next_frame_index < 10 and not bonus_added:
166 next_frame = self.frames[next_frame_index]
167 if len(next_frame) > 0:
168 frame_score += next_frame[0]
169 bonus_added = True
170 next_frame_index += 1
171
172 total_score += frame_score
173
174 # Score 10th frame
175 tenth_frame = self.frames[9]
176 total_score += sum(tenth_frame)
177
178 return total_score
179 # Handled Edge Cases: Attempting to score before game is complete, incomplete frame
Test NameStatus
test_an_unstarted_game_cannot_be_scored
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_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.