weekends

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5
6 Sets up the game state including rolls, current frame, and whether
7 we're in the tenth frame.
8 """
9 self.rolls = []
10 self.current_frame = 1
11 self.is_tenth_frame = False
12 self.frame_rolls = []
13 self.frames = []
14
515 def roll(self, pins):
6 pass
7
16 """
17 Record a roll in the game.
18
19 Args:
20 pins (int): Number of pins knocked down in this roll
21
22 Raises:
23 ValueError: If pins is negative or greater than 10
24 ValueError: If trying to roll after the game is complete
25 ValueError: If frame rules are violated (e.g., more than 10 pins in a frame)
26 """
27 # Edge Case: Invalid pin count (negative or more than 10)
28 if pins < 0 or pins > 10:
29 raise ValueError("Pins must be between 0 and 10")
30
31 # Edge Case: Trying to roll after game is complete
32 if len(self.frames) == 10:
33 raise ValueError("Cannot roll after game is over")
34
35 self.frame_rolls.append(pins)
36 self.rolls.append(pins)
37
38 # Handle tenth frame special cases
39 if self.current_frame == 10:
40 self.is_tenth_frame = True
41
42 # Validate fill balls for tenth frame
43 if len(self.frame_rolls) == 2:
44 # If first roll was a strike, second roll can be anything (0-10)
45 # If first roll wasn't a strike, sum of first two rolls cannot exceed 10
46 if self.frame_rolls[0] != 10 and self.frame_rolls[0] + self.frame_rolls[1] > 10:
47 raise ValueError("invalid fill balls")
48 elif len(self.frame_rolls) == 3:
49 # If we have three rolls, validate based on first two rolls
50 if self.frame_rolls[0] == 10: # First roll was a strike
51 if self.frame_rolls[1] != 10: # Second roll wasn't a strike
52 # Third roll + second roll cannot exceed 10
53 if self.frame_rolls[1] + self.frame_rolls[2] > 10:
54 raise ValueError("invalid fill balls")
55 else:
56 # If first roll wasn't a strike, we shouldn't have a third roll unless second was a spare
57 if self.frame_rolls[0] + self.frame_rolls[1] != 10:
58 raise ValueError("invalid fill balls")
59
60 # Edge Case: Tenth frame has enough rolls to complete
61 if len(self.frame_rolls) == 3 or (len(self.frame_rolls) == 2 and self.frame_rolls[0] + self.frame_rolls[1] < 10):
62 self.frames.append(self.frame_rolls)
63 # Edge Case: Trying to roll after tenth frame is complete
64 if len(self.frames) == 10:
65 return
66 # Handle regular frames
67 else:
68 # Strike case
69 if len(self.frame_rolls) == 1 and self.frame_rolls[0] == 10:
70 self.frames.append(self.frame_rolls)
71 self.current_frame += 1
72 self.frame_rolls = []
73 # Regular frame (2 rolls)
74 elif len(self.frame_rolls) == 2:
75 # Edge Case: Frame total exceeds 10 (invalid)
76 if self.frame_rolls[0] + self.frame_rolls[1] > 10 and self.frame_rolls[0] != 10:
77 raise ValueError("Invalid frame: pins downed exceed 10")
78 self.frames.append(self.frame_rolls)
79 self.current_frame += 1
80 self.frame_rolls = []
81
882 def score(self):
9 pass
83 """
84 Calculate the total score for the game.
85
86 Returns:
87 int: The total score of the game
88
89 Raises:
90 ValueError: If the game is not yet complete (fewer than 10 frames)
91 """
92 # Edge Case: Game not complete (fewer than 10 frames)
93 if len(self.frames) < 10:
94 raise ValueError("Score cannot be taken until the end of the game")
95
96 total_score = 0
97 roll_index = 0
98
99 # Score first 9 frames
100 for frame_index in range(9):
101 frame = self.frames[frame_index]
102
103 # Strike
104 if len(frame) == 1 and frame[0] == 10:
105 total_score += 10 + self._next_two_balls_score(roll_index)
106 roll_index += 1
107 # Spare
108 elif len(frame) == 2 and frame[0] + frame[1] == 10:
109 total_score += 10 + self._next_ball_score(roll_index + 2)
110 roll_index += 2
111 # Open frame
112 else:
113 total_score += frame[0] + frame[1]
114 roll_index += 2
115
116 # Score tenth frame
117 tenth_frame = self.frames[9]
118
119 # Edge Case: Invalid tenth frame (not enough rolls)
120 if len(tenth_frame) < 2:
121 raise ValueError("Invalid tenth frame")
122
123 # Add all rolls in the tenth frame
124 for roll in tenth_frame:
125 total_score += roll
126
127 return total_score
128
129 def _next_ball_score(self, roll_index):
130 """
131 Get the score of the ball at the given roll index.
132
133 Args:
134 roll_index (int): Index of the roll to get
135
136 Returns:
137 int: Score of the ball at the given index
138 """
139 # Return the score of the roll at the given index if it exists
140 if roll_index < len(self.rolls):
141 return self.rolls[roll_index]
142 return 0
143
144 def _next_two_balls_score(self, roll_index):
145 """
146 Get the score of the next two balls after the given roll index.
147
148 Args:
149 roll_index (int): Index of the current roll
150
151 Returns:
152 int: Score of the next two balls
153 """
154 score = 0
155 # Add the next two rolls if they exist
156 if roll_index + 1 < len(self.rolls):
157 score += self.rolls[roll_index + 1]
158 if roll_index + 2 < len(self.rolls):
159 score += self.rolls[roll_index + 2]
160 return score
161
162 # Handled Edge Cases: Invalid pin count, rolling after game completion, frame total exceeding 10,
163 # incomplete game scoring, invalid tenth frame
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.