great

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 # Validate pin count
21 if pins < 0 or pins > 10:
22 raise ValueError("Pins must be between 0 and 10")
23
24 # Check if game is already complete
25 if self._is_game_complete():
26 raise IndexError("Game is already complete")
27
28 # Validate frame rules
29 if self.current_frame < 9: # Frames 0-8 (1-9)
30 # Regular frame validation
31 if self.current_roll_in_frame == 0: # First roll of frame
32 self.rolls.append(pins)
33 if pins == 10: # Strike
34 self.current_frame += 1
35 self.current_roll_in_frame = 0
36 else:
37 self.current_roll_in_frame = 1
38 else: # Second roll of frame
39 # Validate that total pins in frame don't exceed 10
40 if self.rolls[-1] + pins > 10:
41 raise ValueError("Cannot knock down more than 10 pins in a frame")
42 self.rolls.append(pins)
43 self.current_frame += 1
44 self.current_roll_in_frame = 0
45 else: # 10th frame (frame 9)
46 # Special rules for 10th frame
47 if self.current_roll_in_frame == 0: # First roll of 10th frame
48 self.rolls.append(pins)
49 if pins == 10: # Strike
50 self.current_roll_in_frame = 1
51 else:
52 self.current_roll_in_frame = 1
53 elif self.current_roll_in_frame == 1: # Second roll of 10th frame
54 # Check if first roll was a strike
55 if self.rolls[-1] == 10: # Previous roll was a strike
56 # Second roll can be anything 0-10
57 self.rolls.append(pins)
58 self.current_roll_in_frame = 2
59 else: # Previous roll was not a strike
60 # Validate that total doesn't exceed 10
61 if self.rolls[-1] + pins > 10:
62 raise ValueError("Cannot knock down more than 10 pins in a frame")
63 self.rolls.append(pins)
64 if self.rolls[-2] + pins == 10: # Spare
65 self.current_roll_in_frame = 2
66 else: # Open frame
67 self.current_roll_in_frame = 99 # Mark as complete
68 else: # Third roll of 10th frame (fill ball)
69 # Validate fill ball
70 if len(self.rolls) >= 2 and self.rolls[-2] == 10 and self.rolls[-1] != 10:
71 # Strike followed by non-strike, fill ball validation
72 if self.rolls[-1] + pins > 10:
73 raise ValueError("Cannot knock down more than 10 pins in a frame")
74 self.rolls.append(pins)
75 self.current_roll_in_frame = 99 # Mark as complete
76
77 def _is_game_complete(self):
78 """Check if the game is complete."""
79 if self.current_frame < 9: # Haven't reached 10th frame
80 return False
81
82 # In 10th frame
83 if self.current_frame == 9:
84 if self.current_roll_in_frame == 0: # Haven't started 10th frame
85 return False
86 elif self.current_roll_in_frame == 1: # One roll in 10th frame
87 return False
88 elif self.current_roll_in_frame == 2: # Two rolls in 10th frame
89 # Check if we need a third roll
90 if len(self.rolls) >= 2:
91 first_roll = self.rolls[-2] if len(self.rolls) >= 2 else 0
92 second_roll = self.rolls[-1] if len(self.rolls) >= 1 else 0
93 # Need third roll if first was strike or if first+second = spare
94 if first_roll == 10 or first_roll + second_roll == 10:
95 return False
96 return True
97 else: # current_roll_in_frame == 99, game complete
98 return True
99
100 return False
101
8102 def score(self):
9 pass
103 """
104 Calculate the total score for the game.
105
106 Returns:
107 int: The total score of the game.
108
109 Raises:
110 IndexError: If the game is not yet complete.
111 """
112 # Check if game is complete
113 if not self._is_game_complete():
114 raise IndexError("Game is not complete")
115
116 # Calculate score
117 score = 0
118 roll_index = 0
119
120 for frame in range(10):
121 if frame == 9: # 10th frame - special rules
122 # In 10th frame, just sum all rolls
123 if roll_index < len(self.rolls):
124 score += self.rolls[roll_index]
125 if roll_index + 1 < len(self.rolls):
126 score += self.rolls[roll_index + 1]
127 if roll_index + 2 < len(self.rolls):
128 score += self.rolls[roll_index + 2]
129 break
130
131 # Regular frames (1-9)
132 if roll_index >= len(self.rolls):
133 break
134
135 # Strike
136 if self.rolls[roll_index] == 10:
137 # Add 10 plus the next two rolls
138 next_rolls_sum = 0
139 if roll_index + 1 < len(self.rolls):
140 next_rolls_sum += self.rolls[roll_index + 1]
141 if roll_index + 2 < len(self.rolls):
142 next_rolls_sum += self.rolls[roll_index + 2]
143 score += 10 + next_rolls_sum
144 roll_index += 1
145 # Spare
146 elif roll_index + 1 < len(self.rolls) and self.rolls[roll_index] + self.rolls[roll_index + 1] == 10:
147 # Add 10 plus the next roll
148 next_roll = self.rolls[roll_index + 2] if roll_index + 2 < len(self.rolls) else 0
149 score += 10 + next_roll
150 roll_index += 2
151 # Open frame
152 else:
153 frame_score = self.rolls[roll_index]
154 if roll_index + 1 < len(self.rolls):
155 frame_score += self.rolls[roll_index + 1]
156 score += frame_score
157 roll_index += 2
158
159 return score
160
161
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.