lexicon

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 = 0
8 self.current_roll_in_frame = 0
9
510 def roll(self, pins):
6 pass
7
11 """
12 Record a roll in the game.
13
14 Args:
15 pins (int): Number of pins knocked down in this roll.
16
17 Raises:
18 ValueError: If the number of pins is invalid.
19 """
20 # Edge Case: Invalid pin count (negative or more than 10)
21 if pins < 0 or pins > 10:
22 raise ValueError("Pins must be between 0 and 10")
23
24 # Edge Case: Game already complete
25 if self.is_game_complete():
26 raise ValueError("Game has already ended")
27
28 # Edge Case: Too many pins in frame (except for strikes)
29 if self.current_frame < 10:
30 if self.current_roll_in_frame == 1: # Second roll of frame
31 first_roll = self.rolls[-1] if self.rolls else 0
32 if first_roll + pins > 10:
33 raise ValueError("Cannot knock down more than 10 pins in a frame")
34
35 # Edge Case: Invalid fill balls in tenth frame
36 if self.current_frame == 10:
37 self.validate_tenth_frame_roll(pins)
38
39 self.rolls.append(pins)
40 self.update_frame_state()
41
42 def is_game_complete(self):
43 """Check if the game is complete."""
44 if self.current_frame < 10:
45 return False
46
47 # Tenth frame logic
48 # Find the start of the tenth frame based on the number of rolls
49 if len(self.rolls) < 12: # Minimum rolls for a complete game
50 return False
51
52 # Calculate where the tenth frame starts
53 # This depends on how many strikes we had in the first 9 frames
54 tenth_frame_start = 0
55 frame_count = 0
56 i = 0
57
58 while frame_count < 9 and i < len(self.rolls):
59 if self.rolls[i] == 10: # Strike
60 frame_count += 1
61 i += 1
62 else:
63 if i + 1 < len(self.rolls):
64 frame_count += 1
65 i += 2
66 else:
67 break
68
69 tenth_frame_start = i
70 if tenth_frame_start >= len(self.rolls):
71 return False
72
73 tenth_frame_rolls = self.rolls[tenth_frame_start:]
74
75 if len(tenth_frame_rolls) == 2:
76 # Game complete if no strike or spare in tenth frame
77 return tenth_frame_rolls[0] < 10 and tenth_frame_rolls[0] + tenth_frame_rolls[1] < 10
78 elif len(tenth_frame_rolls) == 3:
79 # Game complete after 3 rolls in tenth frame
80 return True
81 elif len(tenth_frame_rolls) == 1:
82 # Tenth frame has only 1 roll - game is not complete
83 return False
84
85 return False
86
87 def validate_tenth_frame_roll(self, pins):
88 """Validate roll in tenth frame."""
89 tenth_frame_start = 18
90 tenth_frame_rolls = self.rolls[tenth_frame_start:]
91
92 if len(tenth_frame_rolls) == 0:
93 # First roll of tenth frame - always valid
94 pass
95 elif len(tenth_frame_rolls) == 1:
96 # Second roll of tenth frame
97 if tenth_frame_rolls[0] == 10: # Strike on first roll
98 # Any pins valid for second roll after strike
99 pass
100 else:
101 # Regular second roll - must not exceed 10
102 if tenth_frame_rolls[0] + pins > 10:
103 raise ValueError("Cannot knock down more than 10 pins in a frame")
104 elif len(tenth_frame_rolls) == 2:
105 # Third roll (fill ball)
106 if tenth_frame_rolls[0] == 10: # Strike on first roll
107 if tenth_frame_rolls[1] == 10: # Strike on second roll
108 # Any pins valid
109 pass
110 else:
111 # Must not exceed 10 with second roll
112 if tenth_frame_rolls[1] + pins > 10:
113 raise ValueError("invalid fill balls")
114 elif tenth_frame_rolls[0] + tenth_frame_rolls[1] == 10: # Spare
115 # Any pins valid for fill ball after spare
116 pass
117 else:
118 # No fill ball should be thrown
119 raise ValueError("Game has already ended")
120
121 def update_frame_state(self):
122 """Update current frame and roll in frame after a roll."""
123 if self.current_frame < 9: # Frames 0-8
124 if self.current_roll_in_frame == 0:
125 # First roll of frame
126 if self.rolls[-1] == 10: # Strike
127 self.current_frame += 1
128 self.current_roll_in_frame = 0
129 else:
130 self.current_roll_in_frame = 1
131 else:
132 # Second roll of frame
133 self.current_frame += 1
134 self.current_roll_in_frame = 0
135 elif self.current_frame == 9: # Ninth frame
136 if self.current_roll_in_frame == 0:
137 # First roll of ninth frame
138 if self.rolls[-1] == 10: # Strike
139 self.current_frame += 1
140 self.current_roll_in_frame = 0
141 else:
142 self.current_roll_in_frame = 1
143 else:
144 # Second roll of ninth frame
145 self.current_frame += 1
146 self.current_roll_in_frame = 0
147 else:
148 # Tenth frame - just increment roll counter
149 self.current_roll_in_frame += 1
150
8151 def score(self):
9 pass
152 """
153 Calculate the total score for the game.
154
155 Returns:
156 int: The total score of the game.
157
158 Raises:
159 ValueError: If the game is not yet complete.
160 """
161 # Edge Case: Game not yet complete
162 if not self.is_game_complete():
163 raise ValueError("Game is not yet complete")
164
165 score = 0
166 roll_index = 0
167
168 for frame in range(10):
169 if self.is_strike(roll_index):
170 # Strike: 10 + next 2 rolls
171 score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
172 roll_index += 1
173 elif self.is_spare(roll_index):
174 # Spare: 10 + next roll
175 score += 10 + self.rolls[roll_index + 2]
176 roll_index += 2
177 else:
178 # Open frame: sum of two rolls
179 score += self.rolls[roll_index] + self.rolls[roll_index + 1]
180 roll_index += 2
181
182 return score
183
184 def is_strike(self, roll_index):
185 """Check if the roll at given index is a strike."""
186 return roll_index < len(self.rolls) and self.rolls[roll_index] == 10
187
188 def is_spare(self, roll_index):
189 """Check if the frame starting at given index is a spare."""
190 return (roll_index + 1 < len(self.rolls) and
191 self.rolls[roll_index] + self.rolls[roll_index + 1] == 10 and
192 self.rolls[roll_index] != 10) # Not a strike
193 # Handled Edge Cases: Invalid pin count, game already completed, exceeding 10 pins in a frame,
194 # game not complete, invalid fill balls, strike/spare scoring rules
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.