| Users / Games | 100 | 10,000 | 1,000,000 | 100,000,000 |
|---|---|---|---|---|
| Concurrent Games | 50 | 5,000 | 500,000 | 50,000,000 |
| Win Checks per Second | 500 | 50,000 | 5,000,000 | 500,000,000 |
| Latency per Check | ~1 ms | ~1 ms | ~5 ms | ~10 ms |
| Memory Usage | Low (MBs) | Moderate (GBs) | High (TBs) | Very High (PBs) |
| Storage for Game States | Minimal | Moderate | Large | Very Large |
Win condition checking in LLD - Scalability & System Analysis
Start learning this pattern below
Jump into concepts and practice - no test required
The first bottleneck is the CPU and memory on the application servers that perform the win condition checks. Each check requires computation to evaluate the game state. As concurrent games increase, the CPU load grows linearly. At around 10,000 concurrent games, a single server cannot keep up with the required checks per second.
- Horizontal Scaling: Add more application servers to distribute win condition checks. Use load balancers to route requests evenly.
- Sharding: Partition games by user or game ID so each server handles a subset of games, reducing per-server load.
- Caching: Cache recent game states and partial results to avoid redundant computations.
- Event-driven Checks: Instead of checking after every move, trigger checks only on relevant state changes to reduce frequency.
- Optimize Algorithms: Use efficient data structures and incremental checks to minimize CPU usage.
- Use In-memory Databases: Store game states in fast in-memory stores like Redis to speed up access.
At 10,000 concurrent games, assuming each game requires 5 win checks per second:
- Total checks per second = 50,000
- Each check takes ~1 ms CPU time -> total CPU time = 50 seconds per second (impossible on one core)
- Need at least 50 CPU cores or 50 servers to handle load
- Memory: Each game state ~10 KB -> 10,000 games = ~100 MB RAM
- Network bandwidth: Minimal for win checks, mostly internal server communication
Start by explaining the core operation: checking win conditions per game move. Then estimate how many checks happen per second at different user scales. Identify the CPU as the first bottleneck due to computation. Discuss horizontal scaling and algorithm optimization as primary solutions. Mention caching and sharding to improve efficiency. Always justify your choices with simple numbers.
Your database handles 1000 QPS for storing game states. Traffic grows 10x to 10,000 QPS. What do you do first?
Answer: The first step is to add read replicas and implement caching to reduce database load. Also, optimize queries and consider sharding the database by game or user ID to distribute load.
Practice
Solution
Step 1: Understand the role of win condition checking
Win condition checking is used to decide if the game has ended with a winner by checking patterns on the board.Step 2: Identify the correct purpose among options
Only To determine if a player has won the game by matching symbols in a row, column, or diagonal describes checking rows, columns, or diagonals for matching symbols to declare a winner.Final Answer:
To determine if a player has won the game by matching symbols in a row, column, or diagonal -> Option BQuick Check:
Win condition checking = Determine winner [OK]
- Confusing win checking with score updating
- Thinking it resets the game board
- Assuming it shows instructions
board?Solution
Step 1: Identify row checking syntax
Checking a row means comparing all elements in the same row index but different columns.Step 2: Match code to row check
if board[row][0] == board[row][1] == board[row][2] != None: compares board[row][0], board[row][1], and board[row][2], which is correct for a row check.Final Answer:
if board[row][0] == board[row][1] == board[row][2] != None: -> Option CQuick Check:
Row check = compare same row elements [OK]
- Mixing row and column indices
- Using != instead of == for equality
- Checking diagonal instead of row
board = [["X", "O", "X"],
["O", "X", "O"],
["O", "X", "X"]]Which of these checks will correctly identify a win for 'X' on the main diagonal?
Solution
Step 1: Identify main diagonal positions
Main diagonal cells are at positions (0,0), (1,1), and (2,2).Step 2: Check which option matches main diagonal and 'X'
board[0][0] == board[1][1] == board[2][2] == "X" compares these exact positions to 'X', correctly checking the main diagonal win.Final Answer:
board[0][0] == board[1][1] == board[2][2] == "X" -> Option AQuick Check:
Main diagonal check = positions (0,0),(1,1),(2,2) [OK]
- Confusing main diagonal with anti-diagonal
- Checking wrong row or column
- Using equality with wrong symbol
def check_column(board, col):
return board[0][col] == board[1][col] == board[2][col]What is the main issue with this code when used for win condition checking?
Solution
Step 1: Analyze the equality check
The code checks if all three cells in the column are equal but does not verify if they are non-empty.Step 2: Identify missing condition for valid win
Without checking for None or empty, it may falsely report a win if all cells are empty.Final Answer:
It does not check if the cells are not empty or None -> Option DQuick Check:
Check for non-empty cells to confirm win [OK]
- Ignoring empty or None cells in equality
- Mixing row and column indices
- Expecting a list return instead of boolean
Solution
Step 1: Understand the cost of checking all lines
Checking all rows, columns, and diagonals after every move is expensive for large boards.Step 2: Focus on last move's related lines
Only the row, column, and diagonals that include the last move can change the win state, so checking these is efficient and scalable.Final Answer:
Only check the row, column, and diagonals related to the last move -> Option AQuick Check:
Check only affected lines after move for efficiency [OK]
- Checking entire board every time wastes resources
- Ignoring diagonals in win checking
- Checking unrelated rows or columns
