lexicon

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5 """
6 self.rolls = []
7
58 def roll(self, pins):
6 pass
7
9 """
10 Record a roll in the game.
11
12 Args:
13 pins (int): Number of pins knocked down in this roll.
14
15 Raises:
16 ValueError: If the number of pins is invalid.
17 IndexError: If the game is already complete.
18 """
19 # Edge Case: Invalid pin count (negative or more than 10)
20 if pins < 0 or pins > 10:
21 raise ValueError("Pins must be between 0 and 10")
22
23 # Edge Case: Game already complete
24 if len(self.rolls) >= 21 or (len(self.rolls) == 20 and not self.is_tenth_frame_special()):
25 raise IndexError("Game is already complete")
26
27 # Edge Case: Invalid pin count for current frame
28 if not self.is_valid_roll(pins):
29 raise ValueError("Frame cannot exceed 10 pins")
30
31 self.rolls.append(pins)
32
33 def is_valid_roll(self, pins):
34 """
35 Check if a roll is valid given the current frame state.
36 """
37 # If no rolls yet, it's always valid
38 if len(self.rolls) == 0:
39 return True
40
41 # Count completed frames to see if we're in the 10th frame
42 completed_frames = 0
43 roll_index = 0
44
45 while roll_index < len(self.rolls) and completed_frames < 10:
46 if self.rolls[roll_index] == 10: # Strike
47 completed_frames += 1
48 roll_index += 1
49 elif roll_index + 1 < len(self.rolls): # Regular frame with 2 rolls
50 completed_frames += 1
51 roll_index += 2
52 else: # First roll of current frame
53 break
54
55 # If we've completed 9 frames, we're in the 10th frame
56 if completed_frames >= 9:
57 # Special rules for tenth frame - no validation needed
58 return True
59
60 # If we're starting a new frame, it's always valid
61 if roll_index == len(self.rolls):
62 return True
63
64 # We're on the second roll of the current frame
65 # Check if the sum would exceed 10
66 first_roll = self.rolls[roll_index]
67 if first_roll + pins > 10:
68 return False
69
70 return True
71
72 def is_tenth_frame_special(self):
73 """
74 Check if the tenth frame requires fill balls (strike or spare).
75 """
76 if len(self.rolls) < 19:
77 return False
78
79 # Tenth frame rolls are at indices 18, 19, 20
80 tenth_frame_first = self.rolls[18]
81
82 # If first roll is a strike, need two more rolls
83 if tenth_frame_first == 10:
84 return len(self.rolls) < 21
85
86 # If first two rolls sum to 10, it's a spare, need one more roll
87 if len(self.rolls) >= 20:
88 tenth_frame_second = self.rolls[19]
89 if tenth_frame_first + tenth_frame_second == 10:
90 return len(self.rolls) < 21
91
92 return False
93
94 def is_game_complete(self):
95 """
96 Check if the game is complete.
97 """
98 # Count completed frames
99 completed_frames = 0
100 roll_index = 0
101
102 while roll_index < len(self.rolls) and completed_frames < 10:
103 if completed_frames == 9: # Tenth frame
104 # Tenth frame is special - can have 1, 2, or 3 rolls
105 if self.rolls[roll_index] == 10: # Strike
106 # Need at least 3 rolls total
107 if len(self.rolls) >= roll_index + 3:
108 completed_frames = 10
109 else:
110 break
111 elif roll_index + 1 < len(self.rolls):
112 if self.rolls[roll_index] + self.rolls[roll_index + 1] == 10: # Spare
113 # Need at least 3 rolls total
114 if len(self.rolls) >= roll_index + 3:
115 completed_frames = 10
116 else:
117 break
118 else: # Open frame
119 # Need exactly 2 rolls
120 if len(self.rolls) >= roll_index + 2:
121 completed_frames = 10
122 else:
123 break
124 else:
125 break
126 else: # Regular frames (0-8)
127 if self.rolls[roll_index] == 10: # Strike
128 completed_frames += 1
129 roll_index += 1
130 elif roll_index + 1 < len(self.rolls): # Regular frame with 2 rolls
131 completed_frames += 1
132 roll_index += 2
133 else: # Incomplete frame
134 break
135
136 return completed_frames >= 10
137
8138 def score(self):
9 pass
139 """
140 Calculate the total score for the game.
141
142 Returns:
143 int: The total score of the game.
144
145 Raises:
146 IndexError: If the game is not yet complete.
147 """
148 # Edge Case: Game not yet complete
149 if not self.is_game_complete():
150 raise IndexError("Game is not complete")
151
152 score = 0
153 roll_index = 0
154
155 for frame in range(10):
156 # Edge Case: Strike in regular frames (first 9)
157 if frame < 9 and self.rolls[roll_index] == 10: # Strike
158 score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
159 roll_index += 1
160 # Edge Case: Strike in tenth frame
161 elif frame == 9 and self.rolls[roll_index] == 10: # Tenth frame strike
162 score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
163 roll_index += 3
164 # Edge Case: Spare in regular frames (first 9)
165 elif frame < 9 and self.rolls[roll_index] + self.rolls[roll_index + 1] == 10: # Spare
166 score += 10 + self.rolls[roll_index + 2]
167 roll_index += 2
168 # Edge Case: Spare in tenth frame
169 elif frame == 9 and self.rolls[roll_index] + self.rolls[roll_index + 1] == 10: # Tenth frame spare
170 score += 10 + self.rolls[roll_index + 2]
171 roll_index += 3
172 else: # Open frame
173 score += self.rolls[roll_index] + self.rolls[roll_index + 1]
174 roll_index += 2
175
176 return score
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_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
test_two_rolls_in_a_frame_cannot_score_more_than_10_points
Pass

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