bohdan

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5
6 The game keeps track of rolls, current frame, and whether the game is finished.
7 """
8 self.rolls = []
9 self.current_frame = 0
10 self.is_finished = False
11 self.frame_rolls = [] # Rolls in the current frame
12
513 def roll(self, pins):
6 pass
7
14 """
15 Record a roll in the game.
16
17 Args:
18 pins (int): Number of pins knocked down in this roll
19
20 Raises:
21 ValueError: If pins is negative or greater than 10
22 IndexError: If the game is already finished
23 """
24 # Edge Case: Game already finished
25 if self.is_finished:
26 raise IndexError("Game is already complete")
27
28 # Edge Case: Invalid number of pins
29 if pins < 0:
30 raise ValueError("Pins must be between 0 and 10")
31
32 # Edge Case: More than 10 pins in a single roll
33 if pins > 10:
34 raise ValueError("Pin count exceeds pins on the lane")
35
36 # Edge Case: In frames 1-9, check if current roll exceeds 10 pins in frame
37 if self.current_frame < 9: # Frames 0-8 (1-9)
38 if len(self.frame_rolls) == 0: # First roll of frame
39 self.frame_rolls.append(pins)
40 # If strike, complete the frame
41 if pins == 10:
42 self.rolls.extend(self.frame_rolls)
43 self.frame_rolls = []
44 self.current_frame += 1
45 else: # Second roll of frame
46 # Edge Case: Sum of pins in frame exceeds 10
47 if self.frame_rolls[0] + pins > 10:
48 raise ValueError("Frame cannot exceed 10 pins")
49 self.frame_rolls.append(pins)
50 self.rolls.extend(self.frame_rolls)
51 self.frame_rolls = []
52 self.current_frame += 1
53 else: # Tenth frame (index 9)
54 self.frame_rolls.append(pins)
55
56 # Check if we need more rolls in the 10th frame
57 if len(self.frame_rolls) == 1:
58 # If first roll is not a strike, continue
59 if pins < 10:
60 return
61 # If first roll is a strike, need two more rolls
62 else:
63 return
64 elif len(self.frame_rolls) == 2:
65 # If first was strike or second completes a spare, need one more roll
66 # Edge Case: Invalid second roll in 10th frame
67 if self.frame_rolls[0] < 10 and self.frame_rolls[0] + self.frame_rolls[1] > 10:
68 raise ValueError("Frame cannot exceed 10 pins")
69
70 # If we have a strike or spare, we need one more roll
71 if self.frame_rolls[0] == 10 or (self.frame_rolls[0] + self.frame_rolls[1]) == 10:
72 return
73 else:
74 # Open frame in 10th, game is over
75 self.rolls.extend(self.frame_rolls)
76 self.is_finished = True
77 return
78 elif len(self.frame_rolls) == 3:
79 # Validate the third roll
80 # Edge Case: Invalid third roll in 10th frame
81 if self.frame_rolls[0] < 10: # First wasn't a strike
82 # Second must have been a spare to get a third roll
83 # Edge Case: Third roll invalid after spare
84 if self.frame_rolls[0] + self.frame_rolls[1] != 10:
85 raise ValueError("invalid fill balls")
86 # Third roll can't knock down more than 10 pins
87 if self.frame_rolls[2] > 10:
88 raise ValueError("Pin count exceeds pins on the lane")
89 else: # First was a strike
90 # Edge Case: Invalid fill balls after strike in 10th frame
91 if self.frame_rolls[1] < 10 and self.frame_rolls[1] + self.frame_rolls[2] > 10:
92 raise ValueError("Pin count exceeds pins on the lane")
93 if self.frame_rolls[1] > 10:
94 raise ValueError("Pin count exceeds pins on the lane")
95
96 self.rolls.extend(self.frame_rolls)
97 self.is_finished = True
98
899 def score(self):
9 pass
100 """
101 Calculate the total score for the game.
102
103 Returns:
104 int: The total score of the game
105
106 Raises:
107 IndexError: If the game is not yet finished
108 """
109 # Edge Case: Game not finished yet
110 if not self.is_finished:
111 raise IndexError("Game is not yet complete")
112
113 total_score = 0
114 roll_index = 0
115
116 # Score frames 1-9
117 for frame in range(9): # Frames 0-8 (1-9)
118 # Edge Case: Not enough rolls to score
119 if roll_index >= len(self.rolls):
120 raise IndexError("Score cannot be taken until the end of the game")
121
122 # Check for strike
123 if self.rolls[roll_index] == 10:
124 # Strike: 10 + next two rolls
125 # Edge Case: Not enough rolls for strike bonus
126 if roll_index + 2 >= len(self.rolls):
127 raise IndexError("Score cannot be taken until the end of the game")
128 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
129 roll_index += 1
130 else:
131 # Check for spare or open frame
132 frame_score = self.rolls[roll_index] + self.rolls[roll_index + 1]
133 # Edge Case: Frame score exceeds 10 (invalid)
134 if frame_score > 10:
135 raise ValueError("invalid frame scores")
136
137 if frame_score == 10:
138 # Spare: 10 + next roll
139 # Edge Case: Not enough rolls for spare bonus
140 if roll_index + 2 >= len(self.rolls):
141 raise IndexError("Score cannot be taken until the end of the game")
142 total_score += 10 + self.rolls[roll_index + 2]
143 else:
144 # Open frame
145 total_score += frame_score
146 roll_index += 2
147
148 # Score 10th frame
149 # Edge Case: Not enough rolls for 10th frame
150 if roll_index >= len(self.rolls):
151 raise IndexError("Score cannot be taken until the end of the game")
152
153 # Add the remaining rolls (could be 2 or 3 rolls in 10th frame)
154 while roll_index < len(self.rolls):
155 total_score += self.rolls[roll_index]
156 roll_index += 1
157
158 return total_score
159 # Handled Edge Cases: Game already finished, invalid number of pins, more than 10 pins in single roll, sum exceeds 10 in frame, invalid second/third roll in 10th frame, game not finished for scoring, not enough rolls to score, frame score exceeds 10, not enough rolls for strike/spare bonus
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.