2660. Determine the Winner of a Bowling Game
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 higher2
if player 2's score is higher0
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.
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:
- Each turn's score is independent once we know the multiplier (1x or 2x)
- The multiplier only depends on the previous two turns
- 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:
- Initialize a score accumulator
s = 0
- Iterate through each turn using
enumerate(arr)
to get both indexi
and valuex
- For each turn, determine the multiplier
k
:- Check if
i > 0
andarr[i-1] == 10
(strike in previous turn) - Check if
i > 1
andarr[i-2] == 10
(strike two turns ago) - If either condition is true, set
k = 2
, otherwisek = 1
- Check if
- Add
k * x
to the running totals
- Return the final score
s
Main Logic:
- Calculate player 1's score:
a = f(player1)
- Calculate player 2's score:
b = f(player2)
- Compare scores and return the result:
- Return
1
ifa > b
(player 1 wins) - Return
2
ifb > a
(player 2 wins) - Return
0
otherwise (tie)
- Return
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 EvaluatorExample 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
, andk
in functionf()
- Variables
a
andb
to store the scores All these variables use constant space that doesn't scale with the input sizen
.
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
, accessingarr[i-1]
will incorrectly access the last element (Python's negative indexing) - When
i = 1
, accessingarr[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
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
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!