let it go

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 or game state is incorrect.
17 """
18 # Validate pin count
19 if pins < 0 or pins > 10:
20 raise ValueError("Pins must be between 0 and 10")
21
22 # Check if game is already complete
23 if self.is_game_complete():
24 raise ValueError("Game is already complete")
25
26 # Validate frame pin count for non-10th frames
27 # For frames 1-9 (rolls 0-17), check that two rolls don't exceed 10 pins
28 # But only if the first roll wasn't a strike (10 pins)
29 if len(self.rolls) < 18: # Not in 10th frame yet
30 frame_index = len(self.rolls) // 2 # Which frame we're in (0-8)
31 roll_in_frame = len(self.rolls) % 2 # Which roll in the frame (0 or 1)
32
33 if frame_index < 9 and roll_in_frame == 1: # Second roll of frames 1-9
34 # Only validate if first roll wasn't a strike
35 if self.rolls[-1] != 10 and self.rolls[-1] + pins > 10:
36 raise ValueError("Cannot knock down more pins than remaining")
37
38 self.rolls.append(pins)
39
40 def is_game_complete(self):
41 """
42 Check if the game is complete.
43
44 Returns:
45 bool: True if the game is complete, False otherwise.
46 """
47 # Count frames properly
48 frame_count = 0
49 roll_index = 0
50
51 while frame_count < 10 and roll_index < len(self.rolls):
52 if frame_count < 9: # Frames 1-9
53 if self.rolls[roll_index] == 10: # Strike
54 roll_index += 1
55 frame_count += 1
56 else: # Not a strike, need two rolls
57 if roll_index + 1 < len(self.rolls):
58 roll_index += 2
59 frame_count += 1
60 else:
61 break # Incomplete frame
62 else: # Frame 10
63 # 10th frame is special
64 if roll_index >= len(self.rolls):
65 break
66
67 if self.rolls[roll_index] == 10: # Strike in 10th frame
68 # Need 2 more rolls
69 if roll_index + 2 < len(self.rolls):
70 roll_index += 3
71 frame_count += 1
72 else:
73 break # Incomplete 10th frame
74 elif roll_index + 1 < len(self.rolls) and self.rolls[roll_index] + self.rolls[roll_index + 1] == 10: # Spare in 10th frame
75 # Need 1 more roll
76 if roll_index + 2 < len(self.rolls):
77 roll_index += 3
78 frame_count += 1
79 else:
80 break # Incomplete 10th frame
81 else: # Open frame in 10th frame
82 if roll_index + 1 < len(self.rolls):
83 roll_index += 2
84 frame_count += 1
85 else:
86 break # Incomplete 10th frame
87
88 return frame_count >= 10 and roll_index >= len(self.rolls)
89
890 def score(self):
9 pass
91 """
92 Calculate the total score for the game.
93
94 Returns:
95 int: The total score of the game.
96
97 Raises:
98 ValueError: If the game is not yet complete.
99 """
100 # Check if game is complete
101 if not self.is_game_complete():
102 raise ValueError("Game is not yet complete")
103
104 total_score = 0
105 roll_index = 0
106
107 for frame in range(10):
108 # Regular frames (0-8)
109 if frame < 9:
110 if self.rolls[roll_index] == 10: # Strike
111 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
112 roll_index += 1
113 elif self.rolls[roll_index] + self.rolls[roll_index + 1] == 10: # Spare
114 total_score += 10 + self.rolls[roll_index + 2]
115 roll_index += 2
116 else: # Open frame
117 total_score += self.rolls[roll_index] + self.rolls[roll_index + 1]
118 roll_index += 2
119 # 10th frame
120 else:
121 # For 10th frame, just add up all the rolls that were made
122 if len(self.rolls) > roll_index:
123 total_score += self.rolls[roll_index]
124 if len(self.rolls) > roll_index + 1:
125 total_score += self.rolls[roll_index + 1]
126 if len(self.rolls) > roll_index + 2:
127 total_score += self.rolls[roll_index + 2]
128
129 return total_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.