seahawk

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5 """
6 self.rolls = []
7 self.current_roll = 0
8
59 def roll(self, pins):
6 pass
7
10 """
11 Record a roll in the game.
12
13 Args:
14 pins (int): Number of pins knocked down in this roll.
15
16 Raises:
17 ValueError: If the number of pins is invalid.
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 completed (10 frames plus any fill balls)
24 if self.is_game_complete():
25 raise IndexError("Game is already complete")
26
27 self.rolls.append(pins)
28 self.current_roll += 1
29
830 def score(self):
9 pass
31 """
32 Calculate the total score for the game.
33
34 Returns:
35 int: The total score of the game.
36
37 Raises:
38 ValueError: If the game is not yet complete.
39 """
40 # Edge Case: Game not yet complete
41 if not self.is_game_complete():
42 raise IndexError("Game is not yet complete")
43
44 score = 0
45 roll_index = 0
46
47 for frame in range(10):
48 # Edge Case: Strike in regular frames (1-9)
49 if self.is_strike(roll_index) and frame < 9:
50 score += 10 + self.strike_bonus(roll_index)
51 roll_index += 1
52 # Edge Case: Spare in regular frames (1-9)
53 elif self.is_spare(roll_index) and frame < 9:
54 score += 10 + self.spare_bonus(roll_index)
55 roll_index += 2
56 # Edge Case: Open frame in regular frames (1-9)
57 elif frame < 9:
58 score += self.sum_of_balls_in_frame(roll_index)
59 roll_index += 2
60 # Edge Case: Tenth frame handling
61 else: # frame == 9
62 # Edge Case: Strike in tenth frame
63 if self.is_strike(roll_index):
64 # Edge Case: Second roll is also a strike
65 if self.is_strike(roll_index + 1):
66 # Edge Case: Third roll must be valid
67 if roll_index + 2 >= len(self.rolls):
68 raise ValueError("Invalid fill balls")
69 score += 10 + 10 + self.rolls[roll_index + 2]
70 else:
71 # Edge Case: Second roll is not a strike
72 if roll_index + 2 >= len(self.rolls):
73 raise ValueError("Invalid fill balls")
74 score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
75 roll_index += 2 # Strike in 10th frame uses 2 rolls in rolls list
76 # Edge Case: Spare in tenth frame
77 elif self.is_spare(roll_index):
78 # Edge Case: Need one more roll for spare fill ball
79 if roll_index + 2 >= len(self.rolls):
80 raise ValueError("Invalid fill balls")
81 score += 10 + self.rolls[roll_index + 2]
82 roll_index += 2
83 # Edge Case: Open tenth frame
84 else:
85 score += self.sum_of_balls_in_frame(roll_index)
86 roll_index += 2
87
88 return score
89
90 def is_game_complete(self):
91 """
92 Check if the game is complete (10 frames have been played).
93
94 Returns:
95 bool: True if the game is complete, False otherwise.
96 """
97 if len(self.rolls) < 12: # Minimum rolls needed (all strikes except last frame)
98 return False
99
100 roll_index = 0
101 frames_completed = 0
102
103 # Count how many frames have been completed
104 for frame in range(10):
105 if frames_completed == 9: # Tenth frame
106 # Edge Case: Strike in tenth frame needs 3 rolls
107 if self.is_strike(roll_index):
108 if roll_index + 2 < len(self.rolls):
109 frames_completed += 1
110 break
111 # Edge Case: Spare in tenth frame needs 3 rolls
112 elif roll_index + 1 < len(self.rolls) and self.is_spare(roll_index):
113 if roll_index + 2 < len(self.rolls):
114 frames_completed += 1
115 break
116 # Edge Case: Open tenth frame needs 2 rolls
117 elif roll_index + 1 < len(self.rolls):
118 frames_completed += 1
119 break
120 else:
121 break
122
123 # Regular frames (1-9)
124 if self.is_strike(roll_index):
125 roll_index += 1
126 else:
127 roll_index += 2
128 frames_completed += 1
129
130 return frames_completed == 10
131
132 def is_strike(self, roll_index):
133 """
134 Check if a roll is a strike.
135
136 Args:
137 roll_index (int): Index of the roll to check.
138
139 Returns:
140 bool: True if the roll is a strike, False otherwise.
141 """
142 # Edge Case: Index out of bounds
143 if roll_index >= len(self.rolls):
144 return False
145 return self.rolls[roll_index] == 10
146
147 def is_spare(self, roll_index):
148 """
149 Check if two rolls form a spare.
150
151 Args:
152 roll_index (int): Index of the first roll to check.
153
154 Returns:
155 bool: True if the two rolls form a spare, False otherwise.
156 """
157 # Edge Case: Index out of bounds
158 if roll_index + 1 >= len(self.rolls):
159 return False
160 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
161
162 def strike_bonus(self, roll_index):
163 """
164 Calculate the bonus for a strike.
165
166 Args:
167 roll_index (int): Index of the strike roll.
168
169 Returns:
170 int: The bonus points for the strike.
171 """
172 # Edge Case: Not enough rolls for bonus calculation
173 if roll_index + 2 >= len(self.rolls):
174 return 0
175 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
176
177 def spare_bonus(self, roll_index):
178 """
179 Calculate the bonus for a spare.
180
181 Args:
182 roll_index (int): Index of the first roll in the spare frame.
183
184 Returns:
185 int: The bonus points for the spare.
186 """
187 # Edge Case: Not enough rolls for bonus calculation
188 if roll_index + 2 >= len(self.rolls):
189 return 0
190 return self.rolls[roll_index + 2]
191
192 def sum_of_balls_in_frame(self, roll_index):
193 """
194 Calculate the sum of pins knocked down in a frame.
195
196 Args:
197 roll_index (int): Index of the first roll in the frame.
198
199 Returns:
200 int: The sum of pins knocked down in the frame.
201 """
202 # Edge Case: Not enough rolls in frame
203 if roll_index + 1 >= len(self.rolls):
204 return 0
205 return self.rolls[roll_index] + self.rolls[roll_index + 1]
206
207 # Handled Edge Cases: Invalid pin count, game already completed, game not yet complete,
208 # strike in regular frames, spare in regular frames, open frame in regular frames,
209 # strike in tenth frame, spare in tenth frame, open tenth frame, second roll is also a strike,
210 # second roll is not a strike, need one more roll for spare fill ball, index out of bounds,
211 # not enough rolls for bonus calculation, not enough rolls in frame
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.