Bird
Raised Fist0
LLDsystem_design~10 mins

Board, Player, Game classes in LLD - Scalability & System Analysis

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Scalability Analysis - Board, Player, Game classes
Growth Table: Board, Player, Game Classes
ScaleUsers / GamesData SizeServer LoadLatencyNotes
100 users~50 concurrent gamesSmall in-memory stateSingle server handles allLow latencySimple in-memory objects, no persistence needed
10,000 users~5,000 concurrent gamesModerate memory, some persistenceMultiple servers, load balancingLow to moderate latencyNeed database for game state, player profiles
1,000,000 users~500,000 concurrent gamesLarge data, distributed storageHorizontal scaling, cachingModerate latencySharding game data, caching hot games
100,000,000 users~50,000,000 concurrent gamesVery large data, multi-regionGlobal load balancing, CDNLow latency via edgeComplex partitioning, eventual consistency
First Bottleneck

At small scale, the in-memory game state works fine. As users grow to thousands, the database storing player and game data becomes the first bottleneck. It struggles with many read/write requests per second. This slows down game updates and player actions.

Scaling Solutions
  • Database scaling: Add read replicas to handle more queries. Use connection pooling to reduce overhead.
  • Caching: Cache frequently accessed game states and player info in memory (e.g., Redis) to reduce DB load.
  • Horizontal scaling: Add more application servers behind a load balancer to handle more concurrent games and players.
  • Sharding: Partition game and player data by user ID or game ID to distribute load across multiple databases.
  • CDN and edge computing: For very large scale, use CDN to serve static assets and edge servers to reduce latency.
Back-of-Envelope Cost Analysis
  • At 10,000 users with 5,000 concurrent games, assuming 1 action per second per game: 5,000 QPS to database.
  • Single PostgreSQL instance handles ~5,000 QPS, so near capacity.
  • Memory per game state ~10 KB, total ~50 MB for 5,000 games in memory.
  • Network bandwidth: 5,000 QPS * 1 KB per request = ~5 MB/s, well within 1 Gbps link.
Interview Tip

Start by describing the components: Board, Player, Game classes and their responsibilities. Then discuss how data grows with users and games. Identify the first bottleneck (usually database). Propose clear, stepwise scaling solutions like caching, read replicas, and sharding. Use numbers to justify your choices. Keep answers structured and focused.

Self Check

Your database handles 1000 QPS. Traffic grows 10x to 10,000 QPS. What do you do first?

Answer: Add read replicas and implement caching to reduce load on the primary database before considering sharding or more complex solutions.

Key Result
The database is the first bottleneck as user and game counts grow; scaling requires caching, read replicas, and sharding to maintain performance.

Practice

(1/5)
1. Which class is primarily responsible for keeping track of the current state of the game board in a typical game design involving Board, Player, and Game classes?
easy
A. Score class
B. Player class
C. Game class
D. Board class

Solution

  1. Step 1: Understand the role of the Board class

    The Board class holds the layout and current state of the game, such as positions of pieces or marks.
  2. Step 2: Compare with Player and Game classes

    The Player class stores player details, and the Game class manages turns and rules, not the board state.
  3. Final Answer:

    Board class -> Option D
  4. Quick Check:

    Board = game state holder [OK]
Hint: Board holds game state, Player holds info, Game controls flow [OK]
Common Mistakes:
  • Confusing Player with Board for state storage
  • Thinking Game class stores board state
  • Assuming Score class manages board
2. Which of the following is the correct way to define a Player class constructor that stores a player's name and ID in a typical object-oriented design?
easy
A. class Player { constructor(name, id) { this.name = name; this.id = id; } }
B. class Player { Player(name, id) { this.name = name; this.id = id; } }
C. function Player(name, id) { this.name = name; this.id = id; }
D. class Player { def __init__(self, name, id): self.name = name; self.id = id }

Solution

  1. Step 1: Identify the correct constructor syntax in JavaScript

    In JavaScript ES6+, the constructor method inside a class is named constructor.
  2. Step 2: Check other options for errors

    class Player { Player(name, id) { this.name = name; this.id = id; } } uses a method named Player instead of constructor; function Player(name, id) { this.name = name; this.id = id; } is a function, not a class; class Player { def __init__(self, name, id): self.name = name; self.id = id } uses Python syntax.
  3. Final Answer:

    class Player { constructor(name, id) { this.name = name; this.id = id; } } -> Option A
  4. Quick Check:

    JavaScript class constructor = constructor() [OK]
Hint: JS class constructors use 'constructor' keyword [OK]
Common Mistakes:
  • Using method named same as class instead of constructor
  • Mixing Python syntax in JavaScript
  • Defining constructor as a separate function
3. Given the following simplified code snippet, what will be the output after calling game.playTurn() once?
class Player {
  constructor(name) { this.name = name; }
}
class Board {
  constructor() { this.state = ['-', '-', '-']; }
  mark(position, symbol) { this.state[position] = symbol; }
}
class Game {
  constructor() {
    this.board = new Board();
    this.players = [new Player('Alice'), new Player('Bob')];
    this.currentPlayerIndex = 0;
  }
  playTurn() {
    const player = this.players[this.currentPlayerIndex];
    this.board.mark(0, this.currentPlayerIndex === 0 ? 'X' : 'O');
    this.currentPlayerIndex = 1 - this.currentPlayerIndex;
    return this.board.state;
  }
}
const game = new Game();
console.log(game.playTurn());
medium
A. ['X', '-', '-']
B. ['O', '-', '-']
C. ['-', '-', '-']
D. Error: mark method not found

Solution

  1. Step 1: Analyze initial state and playTurn logic

    Board state starts as ['-', '-', '-']. Current player index is 0, so symbol 'X' is placed at position 0.
  2. Step 2: Update currentPlayerIndex and return state

    After marking, currentPlayerIndex switches to 1. The returned board state is ['X', '-', '-'].
  3. Final Answer:

    ['X', '-', '-'] -> Option A
  4. Quick Check:

    First turn marks 'X' at position 0 [OK]
Hint: First player marks 'X' at position 0 on first turn [OK]
Common Mistakes:
  • Assuming 'O' is placed first
  • Not updating currentPlayerIndex
  • Confusing board state initialization
4. In the following code snippet, what is the main issue that will cause the game to not switch players correctly?
class Game {
  constructor() {
    this.players = ['Alice', 'Bob'];
    this.currentPlayerIndex = 0;
  }
  nextTurn() {
    this.currentPlayerIndex += 1;
    if (this.currentPlayerIndex > this.players.length) {
      this.currentPlayerIndex = 0;
    }
  }
}
medium
A. Players array should contain Player objects, not strings
B. The condition should be >= players.length, not >
C. currentPlayerIndex should start at 1, not 0
D. nextTurn method should decrement currentPlayerIndex

Solution

  1. Step 1: Understand player index bounds

    Array indices go from 0 to length-1. If currentPlayerIndex equals players.length, it is out of bounds.
  2. Step 2: Check condition for resetting index

    The condition uses > players.length, which misses the case when currentPlayerIndex == players.length, causing an error.
  3. Final Answer:

    The condition should be >= players.length, not > -> Option B
  4. Quick Check:

    Index reset condition must include equality [OK]
Hint: Check array index bounds carefully for off-by-one errors [OK]
Common Mistakes:
  • Using > instead of >= for index reset
  • Ignoring zero-based indexing
  • Thinking player array type causes index error
5. You want to design a turn-based game system using Board, Player, and Game classes. Which design choice best supports adding new game rules and multiple player types (e.g., human, AI) without changing existing code much?
hard
A. Use global variables for player types and rules to simplify access
B. Keep all logic inside Game class and add if-else for player types and rules
C. Use inheritance: create subclasses like HumanPlayer and AIPlayer from Player, and extend Game with rule classes
D. Store all player and rule info in Board class to centralize state

Solution

  1. Step 1: Consider extensibility and separation of concerns

    Inheritance allows creating specialized Player types without modifying base Player class, supporting new behaviors.
  2. Step 2: Use modular design for rules

    Extending Game with separate rule classes or modules keeps code clean and easy to maintain.
  3. Final Answer:

    Use inheritance: create subclasses like HumanPlayer and AIPlayer from Player, and extend Game with rule classes -> Option C
  4. Quick Check:

    Inheritance and modular rules = easy extension [OK]
Hint: Use inheritance and modular rules for easy feature addition [OK]
Common Mistakes:
  • Putting all logic in one class causing messy code
  • Using global variables leading to hard-to-maintain code
  • Storing rules in Board instead of Game