Merry

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 ValueError: 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: Too many pins in current frame
27 # In 10th frame, fill balls are allowed even if they would exceed 10 with previous rolls
28 if (self.frame_rolls and
29 (self.frame_rolls[0] + pins > 10) and
30 self.frame_rolls[0] != 10 and
31 not (len(self.frames) == 9 and len(self.frame_rolls) >= 2)):
32 raise ValueError("Cannot knock down more than 10 pins in a frame")
33
34 # Edge Case: Game already completed
35 if len(self.frames) == 10 and not self._needs_fill_balls():
36 raise ValueError("game already complete")
37
38 # Edge Case: Trying to roll when already have valid 10th frame without needing fill balls
39 if len(self.frames) == 10 and not self._is_tenth_frame_incomplete():
40 raise ValueError("game already complete")
41
42 self.frame_rolls.append(pins)
43 self.rolls.append(pins)
44
45 # Check if frame is complete
46 if self.current_frame < 10:
47 # Regular frames
48 if len(self.frame_rolls) == 2 or self.frame_rolls[0] == 10: # Strike or two rolls
49 self.frames.append(self.frame_rolls[:])
50 self.frame_rolls = []
51 self.current_frame += 1
52 else:
53 # 10th frame
54 if self._is_tenth_frame_complete():
55 self.frames.append(self.frame_rolls[:])
56 self.frame_rolls = []
57
58 def _is_tenth_frame_complete(self):
59 """
60 Check if the 10th frame is complete (including fill balls if needed).
61
62 Returns:
63 bool: True if the 10th frame is complete, False otherwise
64 """
65 if len(self.frame_rolls) < 2:
66 return False
67
68 # If first roll is a strike, need 2 more rolls
69 if self.frame_rolls[0] == 10:
70 return len(self.frame_rolls) == 3
71
72 # If it's a spare, need 1 more roll
73 if sum(self.frame_rolls[:2]) == 10:
74 return len(self.frame_rolls) == 3
75
76 # Otherwise, it's an open frame
77 return len(self.frame_rolls) == 2
78
79 def _is_tenth_frame_incomplete(self):
80 """
81 Check if the 10th frame is incomplete and can still accept more rolls.
82
83 Returns:
84 bool: True if the 10th frame is incomplete, False otherwise
85 """
86 if len(self.frames) < 10:
87 return True
88
89 if len(self.frame_rolls) == 0:
90 return False
91
92 # If we have rolls in progress for frame 10
93 return not self._is_tenth_frame_complete()
94
95 def _needs_fill_balls(self):
96 """
97 Check if the 10th frame needs fill balls.
98
99 Returns:
100 bool: True if fill balls are needed, False otherwise
101 """
102 if len(self.frames) < 10:
103 return False
104
105 if len(self.frame_rolls) == 0:
106 # Check the completed 10th frame
107 tenth_frame = self.frames[9]
108 if len(tenth_frame) < 2:
109 return False
110 return tenth_frame[0] == 10 or sum(tenth_frame[:2]) == 10
111 else:
112 # Check the in-progress 10th frame
113 if len(self.frame_rolls) < 2:
114 return False
115 return self.frame_rolls[0] == 10 or sum(self.frame_rolls[:2]) == 10
116
8117 def score(self):
9 pass
118 """
119 Calculate the total score for the game.
120
121 Returns:
122 int: The total score for the game
123
124 Raises:
125 ValueError: If the game is not yet complete
126 """
127 # Edge Case: Game not complete
128 if len(self.frames) < 10 or (len(self.frames) == 10 and self._is_tenth_frame_incomplete()):
129 raise ValueError("game not complete")
130
131 total_score = 0
132 roll_index = 0
133
134 # Score first 9 frames
135 for frame_num in range(9):
136 if roll_index >= len(self.rolls):
137 raise ValueError("game not complete")
138
139 frame_score = 0
140 first_roll = self.rolls[roll_index]
141
142 if first_roll == 10: # Strike
143 frame_score = 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
144 roll_index += 1
145 else:
146 second_roll = self.rolls[roll_index + 1]
147 frame_score = first_roll + second_roll
148
149 if frame_score == 10: # Spare
150 frame_score += self.rolls[roll_index + 2]
151
152 roll_index += 2
153
154 total_score += frame_score
155
156 # Score 10th frame
157 if roll_index >= len(self.rolls):
158 raise ValueError("game not complete")
159
160 # Add all remaining rolls (the 10th frame rolls)
161 for i in range(roll_index, len(self.rolls)):
162 total_score += self.rolls[i]
163
164 return total_score
165
166 # Handled Edge Cases: Invalid pin count, game already completed, game not 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_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.