awesome

Finished
11class BowlingGame:
22 def __init__(self):
3 pass
3 self.rolls = []
44
55 def roll(self, pins):
6 pass
6 # Edge Case: Game is already over
7 if self.is_game_over():
8 raise ValueError("Cannot roll after game is over")
9
10 # Edge Case: Invalid pin count (negative or more than 10)
11 if pins < 0 or pins > 10:
12 raise ValueError("Pins must be between 0 and 10")
13
14 # Edge Case: Too many pins in a single frame (except in special tenth frame cases)
15 # Simple validation: if the last roll wasn't a strike and we haven't completed 9 frames yet
16 if len(self.rolls) > 0 and self.rolls[-1] != 10:
17 # Count how many frames we've completed so far
18 frames_completed = 0
19 roll_index = 0
20 while roll_index < len(self.rolls) and frames_completed < 9:
21 if self.rolls[roll_index] == 10: # Strike
22 roll_index += 1
23 frames_completed += 1
24 elif roll_index + 1 < len(self.rolls): # Complete frame with 2 rolls
25 roll_index += 2
26 frames_completed += 1
27 else: # Incomplete frame
28 break
29
30 # If we're still in frames 1-9 and the last roll was the first of a frame
31 if frames_completed < 9 and roll_index == len(self.rolls) - 1:
32 if self.rolls[-1] + pins > 10:
33 raise ValueError("Invalid roll: pins downed in a frame cannot exceed 10")
34
35 self.rolls.append(pins)
36
37 def get_frame_start_index(self, frame_num):
38 """Get the starting roll index for a given frame number (0-indexed)"""
39 if frame_num == 0:
40 return 0
41
42 roll_index = 0
43 current_frame = 0
44
45 # Count frames until we reach the target frame
46 while current_frame < frame_num and roll_index < len(self.rolls):
47 if self.rolls[roll_index] == 10: # Strike
48 roll_index += 1
49 else: # Open frame or spare
50 roll_index += 2
51 current_frame += 1
52
53 return roll_index
54
55 def get_current_frame(self):
56 """Calculate the current frame based on number of rolls"""
57 # For frames 1-9, each frame has 1 or 2 rolls
58 # For frame 10, it can have 2 or 3 rolls
59 frame = 0
60 roll_index = 0
61
62 # Count complete frames 1-9
63 while frame < 9 and roll_index < len(self.rolls):
64 if self.rolls[roll_index] == 10: # Strike
65 roll_index += 1
66 else: # Open frame or spare
67 roll_index += 2
68 frame += 1
69
70 return frame
71
72 def is_game_over(self):
73 """Check if the game is over"""
74 # Count frames to see if we've completed 10 frames
75 frame = 0
76 roll_index = 0
77
78 # Process frames 1-9
79 while frame < 9 and roll_index < len(self.rolls):
80 if self.rolls[roll_index] == 10: # Strike
81 roll_index += 1
82 else: # Open frame or spare
83 roll_index += 2
84 frame += 1
85
86 # If we haven't processed 9 complete frames, game is not over
87 if frame < 9:
88 return False
89
90 # Now check the 10th frame
91 if roll_index >= len(self.rolls):
92 return False # No rolls in 10th frame yet
93
94 # 10th frame logic
95 tenth_frame_rolls = []
96 temp_index = roll_index
97 while temp_index < len(self.rolls):
98 tenth_frame_rolls.append(self.rolls[temp_index])
99 temp_index += 1
100
101 # If 10th frame has less than 2 rolls, not over
102 if len(tenth_frame_rolls) < 2:
103 return False
104
105 # If 10th frame is a strike or spare, need 3 rolls
106 if tenth_frame_rolls[0] == 10 or sum(tenth_frame_rolls[:2]) == 10:
107 return len(tenth_frame_rolls) == 3
108 else:
109 # Regular open frame, 2 rolls is enough
110 return len(tenth_frame_rolls) == 2
7111
8112 def score(self):
9 pass
113 # Edge Case: Game is not yet complete
114 if len(self.rolls) > 0 and not self.is_game_over():
115 raise ValueError("Score cannot be taken until the end of the game")
116
117 # If no rolls have been made, score is 0
118 if len(self.rolls) == 0:
119 return 0
120
121 total_score = 0
122 roll_index = 0
123
124 # Score first 9 frames
125 for frame in range(9):
126 # Check if we have enough rolls left
127 if roll_index >= len(self.rolls):
128 break
129
130 # Strike
131 if self.rolls[roll_index] == 10:
132 # Need at least 2 more rolls for bonus
133 if roll_index + 2 < len(self.rolls):
134 total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2]
135 roll_index += 1
136 else:
137 # Check if we have at least 2 rolls for this frame
138 if roll_index + 1 < len(self.rolls):
139 # Spare
140 if self.rolls[roll_index] + self.rolls[roll_index + 1] == 10:
141 # Need at least 1 more roll for bonus
142 if roll_index + 2 < len(self.rolls):
143 total_score += 10 + self.rolls[roll_index + 2]
144 # Open frame
145 else:
146 total_score += self.rolls[roll_index] + self.rolls[roll_index + 1]
147 roll_index += 2
148
149 # Score 10th frame - just sum all rolls in 10th frame
150 while roll_index < len(self.rolls):
151 total_score += self.rolls[roll_index]
152 roll_index += 1
153
154 return total_score
Test NameStatus
test_an_unstarted_game_cannot_be_scored
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_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.