corpus

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5
6 Creates a game with 10 frames, tracking rolls and scores.
7 """
8 self.frames = [[] for _ in range(10)] # 10 frames
9 self.current_frame = 0
10 self.game_over = False
11
512 def roll(self, pins):
6 pass
7
13 """
14 Record a roll in the current frame.
15
16 Args:
17 pins (int): Number of pins knocked down in this roll
18
19 Raises:
20 ValueError: If pins is negative or greater than 10
21 IndexError: If attempting to roll after the game is over
22 """
23 # Edge Case: Invalid pin count (negative or more than 10)
24 if pins < 0 or pins > 10:
25 raise ValueError("invalid pin count")
26
27 # Edge Case: Check if frame would exceed 10 pins (except 10th frame)
28 if self.current_frame < 9: # Only check frames 1-9
29 current_frame_rolls = self.frames[self.current_frame]
30 if len(current_frame_rolls) == 1 and current_frame_rolls[0] != 10:
31 # Second roll in frame, check if total exceeds 10
32 if current_frame_rolls[0] + pins > 10:
33 raise ValueError("invalid pin count")
34
35 # Edge Case: Attempting to roll after game is over
36 if self.game_over:
37 raise IndexError("cannot throw after game is over")
38
39 # Add roll to current frame
40 self.frames[self.current_frame].append(pins)
41
42 # Handle frame advancement logic
43 if self.current_frame < 9: # Frames 1-9
44 # Edge Case: Strike - move to next frame after first roll
45 if len(self.frames[self.current_frame]) == 1 and pins == 10:
46 self.current_frame += 1
47 elif len(self.frames[self.current_frame]) == 2: # Two rolls completed
48 self.current_frame += 1
49 else: # 10th frame or fill balls
50 if self.current_frame == 9: # 10th frame
51 tenth_frame = self.frames[9]
52 if len(tenth_frame) == 1 and pins == 10: # Strike in 10th frame
53 # Add fill ball frame if needed
54 if len(self.frames) == 10:
55 self.frames.append([])
56 self.current_frame = 10
57 elif len(tenth_frame) == 2:
58 # Open frame (less than 10 pins) - game over
59 if tenth_frame[0] + tenth_frame[1] < 10:
60 self.game_over = True
61 # Spare - need fill balls
62 else:
63 # Add fill ball frame if needed
64 if len(self.frames) == 10:
65 self.frames.append([])
66 self.current_frame = 10
67 else: # Fill ball frame (current_frame == 10)
68 fill_frame = self.frames[10]
69 tenth_frame = self.frames[9]
70
71 # Validate fill balls
72 if len(fill_frame) == 1: # First fill ball
73 # If 10th frame was a spare, this is the only fill ball needed
74 if len(tenth_frame) == 2 and tenth_frame[0] + tenth_frame[1] == 10:
75 self.game_over = True
76 # If 10th frame was a strike, need to validate first fill ball
77 elif len(tenth_frame) == 1 and tenth_frame[0] == 10:
78 # First fill ball after strike is always valid
79 pass
80 elif len(fill_frame) == 2: # Second fill ball
81 # Validate that fill balls don't exceed 10 (unless one is a strike)
82 if fill_frame[0] != 10 and fill_frame[0] + fill_frame[1] > 10:
83 # Remove the invalid roll and raise error
84 fill_frame.pop()
85 raise ValueError("invalid fill balls")
86 # For strike in 10th frame: game is over after two fill balls
87 if len(tenth_frame) == 1 and tenth_frame[0] == 10:
88 self.game_over = True
89 elif len(fill_frame) == 3: # Third fill ball attempt (invalid)
90 # Remove the invalid roll and raise error
91 fill_frame.pop()
92 raise ValueError("invalid fill balls")
93
894 def score(self):
9 pass
95 """
96 Calculate the total score for the game.
97
98 Returns:
99 int: Total score of the game
100
101 Raises:
102 IndexError: If the game is not yet complete
103 """
104 # Edge Case: Attempting to score before game is complete
105 # Special case: new game with no rolls should return 0
106 if not self.game_over and self.current_frame == 0 and len(self.frames[0]) == 0:
107 return 0
108
109 if not self.game_over:
110 raise IndexError("game is not yet complete")
111
112 total_score = 0
113
114 # Score frames 1-9
115 for i in range(9):
116 frame = self.frames[i]
117 frame_score = sum(frame)
118
119 # Edge Case: Strike scoring (add next two rolls)
120 if len(frame) == 1 and frame[0] == 10: # Strike
121 # Get next two rolls
122 if i+1 < len(self.frames):
123 next_frame = self.frames[i+1]
124 if len(next_frame) >= 2:
125 frame_score += next_frame[0] + next_frame[1]
126 elif len(next_frame) == 1:
127 # Need one more roll from the frame after that
128 frame_score += next_frame[0]
129 if i+2 < len(self.frames) and len(self.frames[i+2]) >= 1:
130 frame_score += self.frames[i+2][0]
131 # Edge Case: Spare scoring (add next one roll)
132 elif len(frame) == 2 and frame[0] + frame[1] == 10: # Spare
133 if i+1 < len(self.frames) and len(self.frames[i+1]) >= 1:
134 frame_score += self.frames[i+1][0]
135
136 total_score += frame_score
137
138 # Score 10th frame
139 # Edge Case: 10th frame scoring (no bonus)
140 tenth_frame = self.frames[9]
141 total_score += sum(tenth_frame)
142
143 # Add fill ball scores if they exist
144 if len(self.frames) > 10:
145 fill_frame = self.frames[10]
146 total_score += sum(fill_frame)
147
148 return total_score
Test NameStatus
test_an_unstarted_game_cannot_be_scored
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_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.