| 1 | 1 | class BowlingGame: |
| 2 | 2 | def __init__(self): |
| 3 | | pass |
| 4 | | |
| 3 | """ |
| 4 | Initialize a bowling game. |
| 5 | |
| 6 | Sets up the game state including tracking rolls, current frame, |
| 7 | and whether the game is complete. |
| 8 | """ |
| 9 | self.rolls = [] |
| 10 | self.current_frame = 1 |
| 11 | self.frame_rolls = [] |
| 12 | self.frames = [] |
| 13 | self.game_complete = False |
| 14 | |
| 5 | 15 | def roll(self, pins): |
| 6 | | pass |
| 7 | | |
| 16 | """ |
| 17 | Record a roll in the bowling 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 game is already complete |
| 25 | ValueError: If frame rules are violated |
| 26 | """ |
| 27 | # Edge Case: Game already complete |
| 28 | if self.game_complete: |
| 29 | raise ValueError("game already complete") |
| 30 | |
| 31 | # Edge Case: Invalid pin count |
| 32 | if pins < 0 or pins > 10: |
| 33 | raise ValueError("invalid pin count") |
| 34 | |
| 35 | # Edge Case: First roll of a frame with invalid pins after previous roll |
| 36 | if len(self.frame_rolls) == 1 and (self.frame_rolls[0] + pins) > 10 and self.current_frame < 10: |
| 37 | raise ValueError("invalid pin count") |
| 38 | |
| 39 | self.frame_rolls.append(pins) |
| 40 | self.rolls.append(pins) |
| 41 | |
| 42 | # Handle frame completion logic |
| 43 | if self.current_frame < 10: |
| 44 | # Regular frames (1-9) |
| 45 | if pins == 10: # Strike |
| 46 | self.frames.append(self.frame_rolls[:]) |
| 47 | self.frame_rolls = [] |
| 48 | self.current_frame += 1 |
| 49 | elif len(self.frame_rolls) == 2: # Two rolls completed |
| 50 | # Edge Case: Invalid pin count in regular frame |
| 51 | if sum(self.frame_rolls) > 10: |
| 52 | raise ValueError("invalid pin count") |
| 53 | self.frames.append(self.frame_rolls[:]) |
| 54 | self.frame_rolls = [] |
| 55 | self.current_frame += 1 |
| 56 | else: |
| 57 | # 10th frame handling |
| 58 | # Edge Case: Too many rolls in 10th frame |
| 59 | if len(self.frame_rolls) > 3: |
| 60 | raise ValueError("invalid fill balls") |
| 61 | |
| 62 | # Determine if 10th frame is complete |
| 63 | if len(self.frame_rolls) == 2: |
| 64 | # If not a spare or strike, frame is complete |
| 65 | if sum(self.frame_rolls) < 10: |
| 66 | self.frames.append(self.frame_rolls[:]) |
| 67 | self.frame_rolls = [] |
| 68 | self.game_complete = True |
| 69 | elif len(self.frame_rolls) == 3: |
| 70 | # Edge Case: Invalid 10th frame with 3 rolls |
| 71 | # If first two rolls are not 10 or don't sum to 10, third roll shouldn't exist |
| 72 | if self.frame_rolls[0] != 10 and sum(self.frame_rolls[0:2]) != 10: |
| 73 | raise ValueError("invalid fill balls") |
| 74 | # Edge Case: Invalid pin count in 10th frame |
| 75 | if self.frame_rolls[0] == 10 and self.frame_rolls[1] != 10 and (self.frame_rolls[1] + self.frame_rolls[2]) > 10: |
| 76 | raise ValueError("invalid fill balls") |
| 77 | self.frames.append(self.frame_rolls[:]) |
| 78 | self.frame_rolls = [] |
| 79 | self.game_complete = True |
| 80 | |
| 8 | 81 | def score(self): |
| 9 | | pass |
| 82 | """ |
| 83 | Calculate the total score for the bowling game. |
| 84 | |
| 85 | Returns: |
| 86 | int: Total score of the game |
| 87 | |
| 88 | Raises: |
| 89 | ValueError: If the game is not yet complete |
| 90 | """ |
| 91 | # Edge Case: Game not complete |
| 92 | if not self.game_complete and (len(self.frames) < 10 or (len(self.frames) == 10 and self.frame_rolls)): |
| 93 | raise ValueError("game not complete") |
| 94 | |
| 95 | total_score = 0 |
| 96 | roll_index = 0 |
| 97 | |
| 98 | # Score first 9 frames |
| 99 | for frame_num in range(9): |
| 100 | # Edge Case: Not enough frames |
| 101 | if frame_num >= len(self.frames): |
| 102 | raise ValueError("game not complete") |
| 103 | |
| 104 | frame = self.frames[frame_num] |
| 105 | |
| 106 | if len(frame) == 1 and frame[0] == 10: # Strike |
| 107 | # Edge Case: Not enough rolls for strike bonus |
| 108 | if len(self.rolls) <= roll_index + 2: |
| 109 | raise ValueError("game not complete") |
| 110 | total_score += 10 + self.rolls[roll_index + 1] + self.rolls[roll_index + 2] |
| 111 | roll_index += 1 |
| 112 | elif len(frame) == 2 and sum(frame) == 10: # Spare |
| 113 | # Edge Case: Not enough rolls for spare bonus |
| 114 | if len(self.rolls) <= roll_index + 2: |
| 115 | raise ValueError("game not complete") |
| 116 | total_score += 10 + self.rolls[roll_index + 2] |
| 117 | roll_index += 2 |
| 118 | else: # Open frame |
| 119 | total_score += sum(frame) |
| 120 | roll_index += 2 |
| 121 | |
| 122 | # Score 10th frame |
| 123 | # Edge Case: Missing 10th frame |
| 124 | if len(self.frames) < 10: |
| 125 | raise ValueError("game not complete") |
| 126 | |
| 127 | tenth_frame = self.frames[9] |
| 128 | total_score += sum(tenth_frame) |
| 129 | |
| 130 | return total_score |
| 131 | |
| 132 | # Handled Edge Cases: Game already complete, invalid pin count, first roll of frame with invalid pins after previous roll, invalid pin count in regular frame, too many rolls in 10th frame, invalid 10th frame with 3 rolls, invalid pin count in 10th frame, game not complete, not enough frames, not enough rolls for strike bonus, not enough rolls for spare bonus, missing 10th frame |