chris

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 ValueError("game already complete")
26
27 self.rolls.append(pins)
28
829 def score(self):
9 pass
30 """
31 Calculate the total score for the game.
32
33 Returns:
34 int: The total score of the game.
35
36 Raises:
37 ValueError: If the game is not yet complete.
38 """
39 # Edge Case: Game not complete yet
40 if not self.is_game_complete():
41 raise ValueError("game not complete")
42
43 total_score = 0
44 roll_index = 0
45
46 for frame in range(10):
47 # Edge Case: Strike in regular frames (1-9)
48 if self.is_strike(roll_index) and frame < 9:
49 total_score += 10 + self.strike_bonus(roll_index)
50 roll_index += 1
51 # Edge Case: Spare in regular frames (1-9)
52 elif self.is_spare(roll_index) and frame < 9:
53 total_score += 10 + self.spare_bonus(roll_index)
54 roll_index += 2
55 # Edge Case: Open frame in regular frames (1-9)
56 elif frame < 9:
57 total_score += self.sum_of_balls_in_frame(roll_index)
58 roll_index += 2
59 # Edge Case: Tenth frame handling
60 else: # frame == 9 (10th frame)
61 # Edge Case: Strike in 10th frame
62 if self.is_strike(roll_index):
63 # Edge Case: Second roll is also a strike
64 if self.is_strike(roll_index + 1):
65 # Edge Case: Third roll must be valid
66 total_score += 10 + 10 + self.rolls[roll_index + 2]
67 else:
68 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
69 # Edge Case: Spare in 10th frame
70 elif self.is_spare(roll_index):
71 total_score += 10 + self.rolls[roll_index + 2]
72 # Edge Case: Open 10th frame
73 else:
74 total_score += self.sum_of_balls_in_frame(roll_index)
75
76 # For 10th frame, we don't advance roll_index in the same way
77 # since we've already accounted for all rolls
78 break
79
80 return total_score
81
82 def is_game_complete(self):
83 """
84 Check if the game is complete (10 frames have been played).
85
86 Returns:
87 bool: True if the game is complete, False otherwise.
88 """
89 # Edge Case: Not enough rolls to determine completion
90 if len(self.rolls) < 10: # Minimum 10 rolls for 10 strikes
91 return False
92
93 roll_index = 0
94 frame_count = 0
95
96 # Process first 9 frames
97 for frame in range(9):
98 # Edge Case: Strike in regular frames
99 if self.is_strike(roll_index):
100 roll_index += 1
101 else:
102 roll_index += 2
103
104 frame_count += 1
105
106 # Edge Case: Not enough rolls to complete frame
107 if roll_index > len(self.rolls):
108 return False
109
110 # Process 10th frame
111 # Edge Case: Strike in 10th frame
112 if roll_index < len(self.rolls) and self.is_strike(roll_index):
113 # Edge Case: Need two more rolls for fill balls
114 return roll_index + 3 <= len(self.rolls)
115 # Edge Case: Spare in 10th frame
116 elif roll_index + 1 < len(self.rolls) and self.is_spare(roll_index):
117 # Edge Case: Need one more roll for fill ball
118 return roll_index + 3 <= len(self.rolls)
119 # Edge Case: Open 10th frame
120 else:
121 return roll_index + 2 <= len(self.rolls)
122
123 def is_strike(self, roll_index):
124 """
125 Check if a roll is a strike.
126
127 Args:
128 roll_index (int): Index of the roll to check.
129
130 Returns:
131 bool: True if the roll is a strike, False otherwise.
132 """
133 # Edge Case: Index out of bounds
134 if roll_index >= len(self.rolls):
135 return False
136 return self.rolls[roll_index] == 10
137
138 def is_spare(self, roll_index):
139 """
140 Check if two rolls form a spare.
141
142 Args:
143 roll_index (int): Index of the first roll to check.
144
145 Returns:
146 bool: True if the two rolls form a spare, False otherwise.
147 """
148 # Edge Case: Index out of bounds
149 if roll_index + 1 >= len(self.rolls):
150 return False
151 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
152
153 def strike_bonus(self, roll_index):
154 """
155 Calculate the bonus for a strike.
156
157 Args:
158 roll_index (int): Index of the strike roll.
159
160 Returns:
161 int: The bonus points for the strike.
162 """
163 # Edge Case: Not enough rolls for bonus calculation
164 if roll_index + 2 >= len(self.rolls):
165 return 0
166 return self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
167
168 def spare_bonus(self, roll_index):
169 """
170 Calculate the bonus for a spare.
171
172 Args:
173 roll_index (int): Index of the first roll in the spare.
174
175 Returns:
176 int: The bonus points for the spare.
177 """
178 # Edge Case: Not enough rolls for bonus calculation
179 if roll_index + 2 >= len(self.rolls):
180 return 0
181 return self.rolls[roll_index + 2]
182
183 def sum_of_balls_in_frame(self, roll_index):
184 """
185 Calculate the sum of pins knocked down in a frame.
186
187 Args:
188 roll_index (int): Index of the first roll in the frame.
189
190 Returns:
191 int: The sum of pins knocked down in the frame.
192 """
193 # Edge Case: Not enough rolls in frame
194 if roll_index + 1 >= len(self.rolls):
195 return 0
196 return self.rolls[roll_index] + self.rolls[roll_index + 1]
197# Handled Edge Cases: Invalid pin count, game already completed, game not complete yet, strike in regular frames, spare in regular frames, open frame in regular frames, strike in 10th frame, second roll is also a strike, third roll must be valid, spare in 10th frame, open 10th frame, not enough rolls to determine completion, strike in regular frames, not enough rolls to complete frame, index out of bounds, not enough rolls for bonus calculation
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.