xas

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
4
3 """
4 Initialize a new bowling game.
5 """
6 self.rolls = []
7 self.frame_rolls = []
8 self.frames = []
9 self.current_frame = 1
10 self.is_finished = False
11
512 def roll(self, pins):
6 pass
7
13 """
14 Record a roll in the game.
15
16 Args:
17 pins (int): Number of pins knocked down in this roll
18
19 Raises:
20 ValueError: If the game is already finished, pins is negative,
21 or more than 10 pins are knocked down in a single roll
22 IndexError: If attempting to roll after the game has ended
23 """
24 # Edge Case: Game already finished
25 if self.is_finished:
26 raise IndexError("Game has already ended")
27
28 # Edge Case: Invalid pin count (negative or more than 10)
29 if pins < 0 or pins > 10:
30 raise ValueError("Pins must be between 0 and 10")
31
32 # Edge Case: Second roll in frame with invalid total pin count
33 if len(self.frame_rolls) == 1:
34 # For frames 1-9, second roll can't exceed 10 total
35 if self.current_frame < 10 and self.frame_rolls[0] + pins > 10:
36 raise ValueError("Cannot knock down more than 10 pins in a frame")
37 # For frame 10, special rules apply
38 if self.current_frame == 10:
39 # First two rolls can't exceed 10 unless it's a strike followed by non-strike
40 if len(self.frames) == 9 and self.frame_rolls[0] != 10 and self.frame_rolls[0] + pins > 10:
41 raise ValueError("Cannot knock down more than 10 pins in a frame")
42
43 self.rolls.append(pins)
44 self.frame_rolls.append(pins)
45
46 # Handle frame completion
47 if self.current_frame < 10:
48 # Regular frames (1-9)
49 if len(self.frame_rolls) == 2 or self.frame_rolls[0] == 10: # Strike or two rolls
50 self.frames.append(self.frame_rolls[:])
51 self.frame_rolls = []
52 self.current_frame += 1
53 else:
54 # 10th frame - special rules
55 # Edge Case: 10th frame completion conditions
56 if len(self.frame_rolls) == 2:
57 # After 2 rolls in 10th frame
58 if sum(self.frame_rolls) < 10:
59 # Open frame - game finished
60 self.frames.append(self.frame_rolls[:])
61 self.frame_rolls = []
62 self.is_finished = True
63 # else it's a spare - need 1 more roll
64 elif len(self.frame_rolls) == 3:
65 # After 3 rolls in 10th frame (spare or strike with fill balls)
66 self.frames.append(self.frame_rolls[:])
67 self.frame_rolls = []
68 self.is_finished = True
69
870 def score(self):
9 pass
71 """
72 Calculate the total score for the game.
73
74 Returns:
75 int: The total score of the game
76
77 Raises:
78 IndexError: If the game is not yet finished
79 """
80 # Edge Case: Game not finished yet
81 if not self.is_finished:
82 raise IndexError("Game is not yet complete")
83
84 total_score = 0
85 roll_index = 0
86
87 # Score first 9 frames
88 for frame_num in range(9):
89 # Edge Case: Incomplete frames
90 if frame_num >= len(self.frames):
91 raise IndexError("Score cannot be taken until the end of the game")
92
93 frame = self.frames[frame_num]
94
95 if len(frame) >= 1 and frame[0] == 10: # Strike
96 # Add 10 plus next two rolls
97 total_score += 10
98 # Edge Case: Getting next rolls for strike calculation
99 if frame_num + 1 < len(self.frames):
100 next_frame = self.frames[frame_num + 1]
101 if len(next_frame) >= 1:
102 total_score += next_frame[0]
103 if len(next_frame) >= 2:
104 total_score += next_frame[1]
105 else:
106 # Edge Case: Need third roll from frame after next for strike
107 if frame_num + 2 < len(self.frames) and len(self.frames[frame_num + 2]) >= 1:
108 total_score += self.frames[frame_num + 2][0]
109 else:
110 raise IndexError("Score cannot be taken until the end of the game")
111 else:
112 raise IndexError("Score cannot be taken until the end of the game")
113 elif sum(frame[:2]) == 10: # Spare
114 total_score += 10
115 # Add next roll
116 # Edge Case: Getting next roll for spare calculation
117 if frame_num + 1 < len(self.frames) and len(self.frames[frame_num + 1]) >= 1:
118 total_score += self.frames[frame_num + 1][0]
119 else:
120 raise IndexError("Score cannot be taken until the end of the game")
121 else: # Open frame
122 total_score += sum(frame[:2])
123
124 # Score 10th frame
125 # Edge Case: Missing 10th frame
126 if len(self.frames) < 10:
127 raise IndexError("Score cannot be taken until the end of the game")
128
129 tenth_frame = self.frames[9]
130 total_score += sum(tenth_frame)
131
132 return total_score
133
134 # Handled Edge Cases: Game already finished, invalid pin count (negative),
135 # invalid pin count (more than 10), first/second roll invalid pin count,
136 # 10th frame completion conditions, game not finished yet, incomplete frames,
137 # getting next rolls for strike/spare calculation, missing 10th frame
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.