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 trying to roll after the game is complete
21 """
22 # Edge Case: Invalid pin count (negative or more than 10)
23 if pins < 0 or pins > 10:
24 raise ValueError("pins must be between 0 and 10")
25
26 # Edge Case: Game already completed
27 if len(self.frames) == 10 and not self._needs_fill_balls():
28 raise IndexError("cannot roll after game is over")
29
30 # Edge Case: Invalid roll in tenth frame
31 if len(self.frames) == 10 and self._needs_fill_balls():
32 # Check if this is a valid fill ball
33 if not self._is_valid_fill_ball(pins):
34 raise ValueError("invalid fill balls")
35
36 self.frame_rolls.append(pins)
37 self.rolls.append(pins)
38
39 # Check if frame is complete
40 if len(self.frames) < 9: # Frames 1-9
41 if pins == 10 or len(self.frame_rolls) == 2:
42 self.frames.append(self.frame_rolls[:])
43 self.frame_rolls = []
44 elif len(self.frames) == 9: # Tenth frame
45 # Tenth frame is complete when:
46 # - 2 rolls and not a spare/strike
47 # - 3 rolls (after strike/spare)
48 if (len(self.frame_rolls) == 2 and sum(self.frame_rolls) < 10) or len(self.frame_rolls) == 3:
49 self.frames.append(self.frame_rolls[:])
50 self.frame_rolls = []
51
852 def score(self):
9 pass
53 """
54 Calculate the total score for the game.
55
56 Returns:
57 int: The total score for the game
58
59 Raises:
60 IndexError: If the game is not yet complete
61 """
62 # Edge Case: Game not yet complete
63 if len(self.frames) < 10 or self._needs_fill_balls():
64 raise IndexError("Score cannot be taken until the end of the game")
65
66 total = 0
67 roll_index = 0
68
69 for frame_num in range(10):
70 frame = self.frames[frame_num]
71
72 if frame_num < 9: # Frames 1-9
73 if frame[0] == 10: # Strike
74 total += 10 + self._next_two_balls(roll_index)
75 roll_index += 1
76 elif sum(frame) == 10: # Spare
77 total += 10 + self._next_ball(roll_index)
78 roll_index += 2
79 else: # Open frame
80 total += sum(frame)
81 roll_index += 2
82 else: # Tenth frame
83 total += sum(frame)
84
85 return total
86
87 def _next_ball(self, roll_index):
88 """
89 Get the value of the next ball after the given roll index.
90
91 Args:
92 roll_index (int): Index of the current roll
93
94 Returns:
95 int: Value of the next ball
96 """
97 if roll_index + 2 < len(self.rolls):
98 return self.rolls[roll_index + 2]
99 return 0
100
101 def _next_two_balls(self, roll_index):
102 """
103 Get the sum of the next two balls after the given roll index.
104
105 Args:
106 roll_index (int): Index of the current roll
107
108 Returns:
109 int: Sum of the next two balls
110 """
111 total = 0
112 if roll_index + 1 < len(self.rolls):
113 total += self.rolls[roll_index + 1]
114 if roll_index + 2 < len(self.rolls):
115 total += self.rolls[roll_index + 2]
116 return total
117
118 def _needs_fill_balls(self):
119 """
120 Check if the tenth frame needs fill balls.
121
122 Returns:
123 bool: True if fill balls are needed, False otherwise
124 """
125 if len(self.frames) < 10:
126 return False
127 tenth_frame = self.frames[9]
128 # Need fill balls if tenth frame was a strike or spare
129 return len(tenth_frame) < 3 and sum(tenth_frame[:2]) >= 10
130
131 def _is_valid_fill_ball(self, pins):
132 """
133 Check if a fill ball is valid according to bowling rules.
134
135 Args:
136 pins (int): Number of pins knocked down
137
138 Returns:
139 bool: True if valid, False otherwise
140 """
141 if len(self.frames) < 10:
142 return True
143
144 tenth_frame = self.frames[9]
145
146 # If we already have 3 rolls in tenth frame, no more rolls allowed
147 if len(tenth_frame) == 3:
148 return False
149
150 # If we have 2 rolls and it's not a strike or spare, no more rolls allowed
151 if len(tenth_frame) == 2 and sum(tenth_frame) < 10:
152 return False
153
154 # First fill ball
155 if len(tenth_frame) == 2:
156 # If second roll was a strike, first fill ball can be anything
157 if len(tenth_frame) == 2 and tenth_frame[0] == 10:
158 return True
159 # If it's a spare, first fill ball can be anything
160 if sum(tenth_frame) == 10:
161 return True
162
163 # Second fill ball
164 if len(tenth_frame) == 3:
165 # Previous roll was a strike, so this roll is second fill ball
166 if tenth_frame[1] == 10:
167 return True
168 # Previous two rolls sum determines validity
169 if tenth_frame[1] + pins > 10 and tenth_frame[1] != 10:
170 return False
171
172 return True
173
174 # Handled Edge Cases: Invalid pin count, game already completed, invalid roll in tenth frame
175
176# Handled Edge Cases: Game not yet complete
Test NameStatus
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_two_rolls_in_a_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_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_two_bonus_rolls_after_a_strike_in_the_last_frame_can_score_more_than_10_points_if_one_is_a_strike
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.