Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5 """
6 self.rolls = []
7 self.frame_rolls = []
8 self.frames = []
9 self.current_frame = 0
10 self.is_finished = False
11
512 def roll(self, pins):
6 pass
13 """
14 Record a roll in the game.
15
16 Args:
17 pins (int): Number of pins knocked down in this roll
18
19
20 Raises:
21 ValueError: If the game is already finished, or if an invalid number of pins is provided
22 IndexError: If attempting to roll after the game has ended
23 """
24 # Edge Case: Game already finished
25 if self.is_finished:
26 raise IndexError("Game finished")
27
28 # Edge Case: Invalid pin count (negative)
29 if pins < 0:
30 raise ValueError("Pins cannot be negative")
31
32 # Edge Case: Invalid pin count (more than 10)
33 if pins > 10:
34 raise ValueError("Pins cannot be more than 10")
35
736
37 # Edge Case: Invalid pin count in frame (more than remaining pins)
38 if len(self.frame_rolls) == 1:
39 # For frames other than the 10th
40 if self.current_frame < 9 and self.frame_rolls[0] + pins > 10:
41 raise ValueError("Frame cannot have more than 10 pins")
42
43 # For 10th frame, second roll
44 if self.current_frame == 9:
45 # If first roll was a strike, second roll can be up to 10
46 if self.frame_rolls[0] == 10:
47 pass # Valid
48 # If first roll wasn't a strike, sum must not exceed 10
49 elif self.frame_rolls[0] + pins > 10:
50 raise ValueError("Frame cannot have more than 10 pins")
51
52 # For 10th frame, third roll validation (when we already have 2 rolls)
53 if len(self.frame_rolls) == 2 and self.current_frame == 9:
54 # If first roll was a strike, check if second + third > 10
55 # (unless both first and second are strikes, then third can be up to 10)
56 if self.frame_rolls[0] == 10:
57 # If both first and second are strikes, third can be up to 10
58 if self.frame_rolls[1] == 10:
59 pass # Valid
60 # If first is strike but second isn't, check if second + third > 10
61 elif self.frame_rolls[1] + pins > 10:
62 raise ValueError("Frame cannot have more than 10 pins")
63 # If it was a spare (first two rolls sum to 10), third can be up to 10
64 elif self.frame_rolls[0] + self.frame_rolls[1] == 10:
65 pass # Valid
66 # If it's an open frame, we shouldn't reach here
67 else:
68 # This shouldn't happen in a valid game
69 pass
70
71 self.frame_rolls.append(pins)
72 self.rolls.append(pins)
73
74 # For 10th frame, third roll validation (after appending)
75 if len(self.frame_rolls) == 3 and self.current_frame == 9:
76 # If first roll was a strike, check if second + third > 10
77 # (unless both first and second are strikes, then third can be up to 10)
78 if self.frame_rolls[0] == 10:
79 # If both first and second are strikes, third can be up to 10
80 if self.frame_rolls[1] == 10:
81 pass # Valid
82 # If first is strike but second isn't, check if second + third > 10
83 elif self.frame_rolls[1] + self.frame_rolls[2] > 10:
84 # Remove the invalid roll
85 self.frame_rolls.pop()
86 self.rolls.pop()
87 raise ValueError("Frame cannot have more than 10 pins")
88 # If it was a spare (first two rolls sum to 10), third can be up to 10
89 elif self.frame_rolls[0] + self.frame_rolls[1] == 10:
90 pass # Valid
91 # If it's an open frame, we shouldn't reach here
92 else:
93 # This shouldn't happen in a valid game
94 pass
95
96 # Handle frame completion
97 if self.current_frame < 9: # Frames 1-9
98 # Strike frame (only one roll)
99 if len(self.frame_rolls) == 1 and pins == 10:
100 self.frames.append(self.frame_rolls[:])
101 self.frame_rolls = []
102 self.current_frame += 1
103 # Regular frame (two rolls)
104 elif len(self.frame_rolls) == 2:
105 self.frames.append(self.frame_rolls[:])
106 self.frame_rolls = []
107 self.current_frame += 1
108 else: # 10th frame
109 # If it's an open frame (no strike or spare), game ends after 2 rolls
110 if len(self.frame_rolls) == 2 and (self.frame_rolls[0] + self.frame_rolls[1]) < 10:
111 self.frames.append(self.frame_rolls[:])
112 self.frame_rolls = []
113 self.is_finished = True
114 # If we have 3 rolls in 10th frame, game is finished
115 elif len(self.frame_rolls) == 3:
116 self.frames.append(self.frame_rolls[:])
117 self.frame_rolls = []
118 self.is_finished = True
119 # Otherwise, we need more rolls (strike or spare situation)
120 else:
121 # Continue playing - need more rolls
122 pass
123
8124 def score(self):
9 pass
125 """
126 Calculate the total score for the game.
127
128
129 Returns:
130 int: The total score for the game
131
132
133 Raises:
134 IndexError: If the game is not yet finished
135 """
136 # Edge Case: Game not finished yet
137 if not self.is_finished:
138 raise IndexError("Game not finished")
139
140
141 total_score = 0
142 roll_index = 0
143
144 # Score first 9 frames
145 for frame_index in range(9):
146 # Strike
147 if self.rolls[roll_index] == 10:
148 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
149 roll_index += 1
150 # Spare
151 elif self.rolls[roll_index] + self.rolls[roll_index + 1] == 10:
152 total_score += 10 + self.rolls[roll_index + 2]
153 roll_index += 2
154 # Open frame
155 else:
156 total_score += self.rolls[roll_index] + self.rolls[roll_index + 1]
157 roll_index += 2
158
159 # Score 10th frame
160 # Add all rolls in the 10th frame
161 for i in range(len(self.frames[9])):
162 total_score += self.frames[9][i]
163
164
165 return total_score
166
167 # Handled Edge Cases: Game already finished, invalid pin count (negative or more than 10), invalid pin count in frame (more than remaining pins), game not finished yet
168 # Handled Edge Cases: Game not finished yet
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.