relax

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 self.game_complete = False
10
511 def roll(self, pins):
6 pass
7
12 """
13 Record a roll in the game.
14
15 :param pins: Number of pins knocked down in this roll.
16 :raises ValueError: If the number of pins is invalid.
17 :raises IndexError: If the game is already complete.
18 """
19 # Edge Case: Game already complete
20 if self.game_complete:
21 raise IndexError("Game is already complete")
22
23 # Edge Case: Invalid number of pins (negative)
24 if pins < 0:
25 raise ValueError("Pins must be between 0 and 10")
26
27 # For 10th frame fill balls, allow more than 10 pins (will be validated during scoring)
28 if self.current_frame < 9 or (self.current_frame == 9 and self.current_roll_in_frame < 2):
29 if pins > 10:
30 raise ValueError("Pins must be between 0 and 10")
31
32 # Edge Case: Too many pins in a frame (except for 10th frame fill balls)
33 if self.current_frame < 9: # Regular frames (0-8)
34 if self.current_roll_in_frame == 1 and self.rolls[-1] + pins > 10:
35 raise ValueError("Cannot knock down more than 10 pins in a frame")
36 elif self.current_frame == 9: # 10th frame
37 # Special handling for 10th frame
38 if self.current_roll_in_frame == 1:
39 # Second roll in 10th frame
40 if self.rolls[-1] == 10: # First roll was a strike
41 # After a strike, second roll can be anything 0-10
42 pass
43 elif self.rolls[-1] + pins > 10:
44 raise ValueError("Cannot knock down more than 10 pins in a frame")
45 elif self.current_roll_in_frame == 2:
46 # Third roll in 10th frame (only if first two were strikes or spare)
47 if not (self.rolls[-2] == 10 or self.rolls[-2] + self.rolls[-1] == 10):
48 raise ValueError("Cannot knock down more than 10 pins in a frame")
49
50 self.rolls.append(pins)
51 self.current_roll_in_frame += 1
52
53 # Update frame tracking
54 if self.current_frame < 9: # Regular frames (0-8)
55 if self.current_roll_in_frame == 2 or pins == 10: # End of frame (2 rolls or strike)
56 self.current_frame += 1
57 self.current_roll_in_frame = 0
58 else: # 10th frame
59 # Special handling for 10th frame
60 if self.current_roll_in_frame == 2:
61 # Check if we need a third roll
62 if self.rolls[-2] == 10 or self.rolls[-2] + self.rolls[-1] == 10:
63 # Need a fill ball
64 pass
65 else:
66 # No fill ball needed
67 self.game_complete = True
68 elif self.current_roll_in_frame == 3:
69 # Third roll always ends the game
70 self.game_complete = True
71
872 def score(self):
9 pass
73 """
74 Calculate the total score for the game.
75
76 :return: Total score of the game.
77 :raises IndexError: If the game is not complete.
78 :raises ValueError: If there are invalid fill balls.
79 """
80 # Edge Case: Game not complete
81 if not self.game_complete:
82 raise IndexError("Game is not yet complete")
83
84 # Validate fill balls in 10th frame
85 if self.current_frame == 9 and len(self.rolls) >= 3:
86 # Check if any roll in 10th frame has more than 10 pins
87 frame_rolls = self.rolls[-3:] if len(self.rolls) >= 3 else self.rolls[-2:]
88 for roll in frame_rolls:
89 if roll > 10:
90 raise ValueError("invalid fill balls")
91
92 score = 0
93 roll_index = 0
94
95 for frame in range(10):
96 if self._is_strike(roll_index): # Strike
97 score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
98 if frame < 9: # Not 10th frame
99 roll_index += 1
100 else: # 10th frame
101 roll_index += 3 # Strike in 10th frame uses all 3 rolls
102 elif self._is_spare(roll_index): # Spare
103 score += 10 + self.rolls[roll_index + 2]
104 if frame < 9: # Not 10th frame
105 roll_index += 2
106 else: # 10th frame
107 roll_index += 3 # Spare in 10th frame uses all 3 rolls (2 for frame + 1 bonus)
108 else: # Open frame
109 score += self.rolls[roll_index] + self.rolls[roll_index + 1]
110 roll_index += 2
111
112 return score
113
114 def _is_strike(self, roll_index):
115 """
116 Check if a roll is a strike.
117
118 :param roll_index: Index of the roll to check.
119 :return: True if the roll is a strike, False otherwise.
120 """
121 return self.rolls[roll_index] == 10
122
123 def _is_spare(self, roll_index):
124 """
125 Check if a frame is a spare.
126
127 :param roll_index: Index of the first roll in the frame.
128 :return: True if the frame is a spare, False otherwise.
129 """
130 return self.rolls[roll_index] + self.rolls[roll_index + 1] == 10
131
132 # Handled Edge Cases: Invalid number of pins, too many pins in a frame, game not complete,
133 # not enough rolls to calculate score, not enough rolls after strike/spare to calculate score,
134 # extra rolls after 10th frame that shouldn't exist
Test NameStatus
test_second_bonus_roll_after_a_strike_in_the_last_frame_cannot_score_more_than_10_points
Fail
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_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.