Facebook Pixel

2660. Determine the Winner of a Bowling Game

EasyArraySimulation
Leetcode Link

Problem Description

You are given two arrays player1 and player2 representing the number of pins knocked down by each player in a bowling game. Each array has n elements, where each element represents the pins knocked down in that turn (maximum 10 pins per turn).

The scoring works with a special bonus rule:

  • If a player knocks down 10 pins (a strike) in any turn, then for the next two turns, their points are doubled
  • Specifically, for turn i, the score is:
    • 2 * xi if the player got a strike in turn (i-1) OR turn (i-2)
    • xi otherwise (normal scoring)

The total score for each player is the sum of all their turn values after applying the bonus rule.

Your task is to compare the final scores and return:

  • 1 if player 1's score is higher
  • 2 if player 2's score is higher
  • 0 if the scores are tied

For example, if a player's turns are [10, 2, 3]:

  • Turn 0: score = 10 (no previous turns, so no bonus)
  • Turn 1: score = 2 * 2 = 4 (doubled because of strike in turn 0)
  • Turn 2: score = 2 * 3 = 6 (doubled because of strike in turn 0, which is within 2 turns)
  • Total score = 10 + 4 + 6 = 20

The solution calculates each player's score by checking the previous two turns for strikes and applying the appropriate multiplier, then compares the final scores to determine the winner.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we need to track when strikes (10 pins) occur because they affect scoring for the next two turns. Since the bonus only depends on whether there was a strike in the previous one or two turns, we can process each turn sequentially while looking back.

For each turn i, we only need to check:

  • Did turn i-1 have a strike?
  • Did turn i-2 have a strike?

If either answer is yes, we double the current turn's score. This is a straightforward simulation - we don't need any complex data structures or algorithms.

The approach becomes clear when we realize:

  1. Each turn's score is independent once we know the multiplier (1x or 2x)
  2. The multiplier only depends on the previous two turns
  3. We can calculate each player's total score independently

Since both players follow the same scoring rules, we can write a single function f(arr) that takes any player's array and returns their total score. The function iterates through each turn, checks if either of the two previous turns was a strike (value of 10), applies the appropriate multiplier (k = 2 if there was a recent strike, otherwise k = 1), and accumulates the score.

Once we have both scores, comparing them is trivial - just return 1, 2, or 0 based on which is larger. The elegance of this solution lies in its simplicity: we're directly simulating the scoring rules without any unnecessary complexity.

Solution Approach

The solution implements a simulation approach by creating a helper function to calculate scores and then comparing them.

Helper Function f(arr):

The core logic resides in the function f(arr) which calculates the total score for a given player's array:

  1. Initialize a score accumulator s = 0
  2. Iterate through each turn using enumerate(arr) to get both index i and value x
  3. For each turn, determine the multiplier k:
    • Check if i > 0 and arr[i-1] == 10 (strike in previous turn)
    • Check if i > 1 and arr[i-2] == 10 (strike two turns ago)
    • If either condition is true, set k = 2, otherwise k = 1
  4. Add k * x to the running total s
  5. Return the final score s

Main Logic:

  1. Calculate player 1's score: a = f(player1)
  2. Calculate player 2's score: b = f(player2)
  3. Compare scores and return the result:
    • Return 1 if a > b (player 1 wins)
    • Return 2 if b > a (player 2 wins)
    • Return 0 otherwise (tie)

Key Implementation Details:

  • The condition (i and arr[i - 1] == 10) is a compact way to check if we're not at index 0 AND the previous turn was a strike
  • The condition (i > 1 and arr[i - 2] == 10) ensures we're at least at index 2 before checking two turns back
  • The use of the ternary operator k = 2 if ... else 1 cleanly handles the multiplier logic
  • The nested ternary in the return statement 1 if a > b else (2 if b > a else 0) elegantly handles all three possible outcomes

The time complexity is O(n) where n is the number of turns, as we iterate through each array once. The space complexity is O(1) as we only use a few variables to track scores.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example with player1 = [3, 10, 5, 2] and player2 = [10, 4, 7, 3].

Calculating Player 1's Score:

  • Turn 0 (value=3): No previous turns to check for strikes

    • Multiplier k = 1
    • Score = 1 × 3 = 3
    • Running total = 3
  • Turn 1 (value=10): Check previous turns

    • Turn 0 had value 3 (not a strike)
    • Multiplier k = 1
    • Score = 1 × 10 = 10
    • Running total = 3 + 10 = 13
  • Turn 2 (value=5): Check previous turns

    • Turn 1 had value 10 (strike!) → k should be 2
    • Turn 0 had value 3 (not a strike)
    • Multiplier k = 2 (because of strike in turn 1)
    • Score = 2 × 5 = 10
    • Running total = 13 + 10 = 23
  • Turn 3 (value=2): Check previous turns

    • Turn 2 had value 5 (not a strike)
    • Turn 1 had value 10 (strike!) → k should be 2
    • Multiplier k = 2 (because of strike in turn 1, which is 2 turns back)
    • Score = 2 × 2 = 4
    • Running total = 23 + 4 = 27

Calculating Player 2's Score:

  • Turn 0 (value=10): No previous turns

    • Multiplier k = 1
    • Score = 1 × 10 = 10
    • Running total = 10
  • Turn 1 (value=4): Check previous turns

    • Turn 0 had value 10 (strike!) → k should be 2
    • Multiplier k = 2
    • Score = 2 × 4 = 8
    • Running total = 10 + 8 = 18
  • Turn 2 (value=7): Check previous turns

    • Turn 1 had value 4 (not a strike)
    • Turn 0 had value 10 (strike!) → k should be 2
    • Multiplier k = 2 (because of strike in turn 0, which is 2 turns back)
    • Score = 2 × 7 = 14
    • Running total = 18 + 14 = 32
  • Turn 3 (value=3): Check previous turns

    • Turn 2 had value 7 (not a strike)
    • Turn 1 had value 4 (not a strike)
    • Multiplier k = 1
    • Score = 1 × 3 = 3
    • Running total = 32 + 3 = 35

Final Comparison:

  • Player 1 total: 27
  • Player 2 total: 35
  • Since 35 > 27, return 2 (Player 2 wins)

Solution Implementation

1class Solution:
2    def isWinner(self, player1: List[int], player2: List[int]) -> int:
3        """
4        Determines the winner between two players based on their scores.
5        Returns 1 if player1 wins, 2 if player2 wins, 0 if tie.
6        """
7      
8        def calculate_score(pins: List[int]) -> int:
9            """
10            Calculates the total score for a player.
11            If a player hits 10 pins in round i-1 or i-2, 
12            the score for round i is doubled.
13          
14            Args:
15                pins: List of pins knocked down in each round
16              
17            Returns:
18                Total score after applying bonus rules
19            """
20            total_score = 0
21          
22            for round_index, pins_knocked in enumerate(pins):
23                # Check if bonus multiplier applies (2x if previous round(s) had 10 pins)
24                has_bonus = False
25              
26                # Check if previous round was a strike (10 pins)
27                if round_index >= 1 and pins[round_index - 1] == 10:
28                    has_bonus = True
29              
30                # Check if two rounds ago was a strike (10 pins)
31                if round_index >= 2 and pins[round_index - 2] == 10:
32                    has_bonus = True
33              
34                # Apply multiplier (2 if bonus, otherwise 1)
35                multiplier = 2 if has_bonus else 1
36                total_score += multiplier * pins_knocked
37              
38            return total_score
39      
40        # Calculate scores for both players
41        player1_score = calculate_score(player1)
42        player2_score = calculate_score(player2)
43      
44        # Determine and return the winner
45        if player1_score > player2_score:
46            return 1
47        elif player2_score > player1_score:
48            return 2
49        else:
50            return 0
51
1class Solution {
2    /**
3     * Determines the winner between two players based on their scores.
4     * @param player1 Array of scores for player 1
5     * @param player2 Array of scores for player 2
6     * @return 1 if player1 wins, 2 if player2 wins, 0 if tie
7     */
8    public int isWinner(int[] player1, int[] player2) {
9        int scorePlayer1 = calculateScore(player1);
10        int scorePlayer2 = calculateScore(player2);
11      
12        // Compare scores and return winner
13        if (scorePlayer1 > scorePlayer2) {
14            return 1;
15        } else if (scorePlayer2 > scorePlayer1) {
16            return 2;
17        } else {
18            return 0;
19        }
20    }
21
22    /**
23     * Calculates the total score for a player.
24     * If a 10 was scored in the previous 1 or 2 turns, current score is doubled.
25     * @param scores Array of individual scores
26     * @return Total calculated score
27     */
28    private int calculateScore(int[] scores) {
29        int totalScore = 0;
30      
31        for (int i = 0; i < scores.length; i++) {
32            // Check if previous turn (i-1) or turn before that (i-2) had a score of 10
33            boolean hasPreviousTen = (i > 0 && scores[i - 1] == 10);
34            boolean hasTwoTurnsBackTen = (i > 1 && scores[i - 2] == 10);
35          
36            // Apply multiplier: 2x if there was a 10 in previous two turns, 1x otherwise
37            int multiplier = (hasPreviousTen || hasTwoTurnsBackTen) ? 2 : 1;
38          
39            // Add current score with multiplier to total
40            totalScore += multiplier * scores[i];
41        }
42      
43        return totalScore;
44    }
45}
46
1class Solution {
2public:
3    int isWinner(vector<int>& player1, vector<int>& player2) {
4        // Lambda function to calculate the total score for a player
5        auto calculateScore = [](vector<int>& pins) {
6            int totalScore = 0;
7            int n = pins.size();
8          
9            // Iterate through each frame
10            for (int i = 0; i < n; ++i) {
11                // Check if current frame should have double points
12                // Double points apply if either of the previous two frames was a strike (10 pins)
13                bool isPreviousStrike = (i >= 1 && pins[i - 1] == 10);
14                bool isSecondPreviousStrike = (i >= 2 && pins[i - 2] == 10);
15              
16                // Multiplier is 2 if either of the previous two frames was a strike, otherwise 1
17                int multiplier = (isPreviousStrike || isSecondPreviousStrike) ? 2 : 1;
18              
19                // Add the current frame's score with the appropriate multiplier
20                totalScore += multiplier * pins[i];
21            }
22          
23            return totalScore;
24        };
25      
26        // Calculate scores for both players
27        int player1Score = calculateScore(player1);
28        int player2Score = calculateScore(player2);
29      
30        // Determine the winner
31        // Return 1 if player1 wins, 2 if player2 wins, 0 if tie
32        if (player1Score > player2Score) {
33            return 1;
34        } else if (player2Score > player1Score) {
35            return 2;
36        } else {
37            return 0;
38        }
39    }
40};
41
1/**
2 * Determines the winner between two players based on their scores with bonus rules.
3 * @param player1 - Array of scores for player 1
4 * @param player2 - Array of scores for player 2
5 * @returns 1 if player1 wins, 2 if player2 wins, 0 if tie
6 */
7function isWinner(player1: number[], player2: number[]): number {
8    /**
9     * Calculates the total score for a player with bonus rules.
10     * If the previous pin (i-1) or the one before that (i-2) is 10,
11     * the current score is doubled.
12     * @param scores - Array of scores to calculate
13     * @returns Total score with bonuses applied
14     */
15    const calculateTotalScore = (scores: number[]): number => {
16        let totalScore = 0;
17      
18        for (let index = 0; index < scores.length; index++) {
19            // Add the base score
20            totalScore += scores[index];
21          
22            // Check if bonus applies (previous or second previous score is 10)
23            const hasPreviousTen = index > 0 && scores[index - 1] === 10;
24            const hasSecondPreviousTen = index > 1 && scores[index - 2] === 10;
25          
26            if (hasPreviousTen || hasSecondPreviousTen) {
27                // Double the current score as bonus
28                totalScore += scores[index];
29            }
30        }
31      
32        return totalScore;
33    };
34  
35    // Calculate final scores for both players
36    const player1TotalScore = calculateTotalScore(player1);
37    const player2TotalScore = calculateTotalScore(player2);
38  
39    // Determine and return the winner
40    if (player1TotalScore > player2TotalScore) {
41        return 1;
42    } else if (player1TotalScore < player2TotalScore) {
43        return 2;
44    } else {
45        return 0; // Tie
46    }
47}
48

Time and Space Complexity

The time complexity is O(n), where n is the length of the input arrays. The algorithm iterates through each array exactly once in the helper function f(). For each element, it performs constant-time operations (checking previous elements for value 10 and calculating the score). Since the function f() is called twice (once for each player), the total time complexity remains O(n) + O(n) = O(n).

The space complexity is O(1). The algorithm only uses a fixed amount of extra space regardless of the input size. The variables used include:

  • Local variables s, i, x, and k in function f()
  • Variables a and b to store the scores All these variables use constant space that doesn't scale with the input size n.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Boundary Checking for Previous Turns

A common mistake is not properly checking array boundaries when looking back at previous turns for strikes. Developers might write:

Incorrect Code:

# WRONG - This will cause IndexError or incorrect logic
if arr[i-1] == 10 or arr[i-2] == 10:
    multiplier = 2

Why it's wrong:

  • When i = 0, accessing arr[i-1] will incorrectly access the last element (Python's negative indexing)
  • When i = 1, accessing arr[i-2] will also incorrectly access from the end of the array

Correct Solution:

has_bonus = False
if i >= 1 and arr[i-1] == 10:
    has_bonus = True
if i >= 2 and arr[i-2] == 10:
    has_bonus = True
multiplier = 2 if has_bonus else 1

2. Using AND Instead of OR Logic

Another pitfall is misunderstanding the bonus condition and checking if BOTH previous turns were strikes:

Incorrect Code:

# WRONG - Requires both turns to be strikes
if i >= 2 and arr[i-1] == 10 and arr[i-2] == 10:
    multiplier = 2

Why it's wrong: The bonus applies if EITHER of the previous two turns was a strike, not if both were strikes.

Correct Solution:

# Check each condition separately with OR logic
if (i >= 1 and arr[i-1] == 10) or (i >= 2 and arr[i-2] == 10):
    multiplier = 2

3. Off-by-One Error in Turn Counting

Some might miscount which previous turns to check:

Incorrect Code:

# WRONG - Checking wrong turns
if i > 0 and arr[i] == 10:  # Checking current turn instead of previous
    multiplier = 2
# OR
if i >= 3 and arr[i-3] == 10:  # Checking too far back
    multiplier = 2

Why it's wrong: The bonus only applies for strikes in turns i-1 and i-2, not the current turn or turns further back.

Correct Solution:

# Only check i-1 and i-2
if (i >= 1 and arr[i-1] == 10) or (i >= 2 and arr[i-2] == 10):
    multiplier = 2

4. Forgetting to Reset or Initialize Variables

When using a flag variable, forgetting to reset it for each turn:

Incorrect Code:

has_bonus = False  # Initialized outside the loop
for i, pins in enumerate(arr):
    if i >= 1 and arr[i-1] == 10:
        has_bonus = True  # Never reset to False
    # ... rest of logic

Why it's wrong: Once has_bonus becomes True, it stays True for all subsequent turns.

Correct Solution:

for i, pins in enumerate(arr):
    has_bonus = False  # Reset for each turn
    if i >= 1 and arr[i-1] == 10:
        has_bonus = True
    if i >= 2 and arr[i-2] == 10:
        has_bonus = True
    multiplier = 2 if has_bonus else 1
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.

Which of the following method should we use to solve this problem?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More