Bird
Raised Fist0

Snake and Ladder Game

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
๐ŸŽฏ
Snake and Ladder Game Design
40 min ยท medium
medium

Design a Snake and Ladder game with a board of 100 cells, multiple snakes and ladders, 2-4 players, and a dice that can be customized for different rolling strategies.

  • Board has 100 cells numbered 1 to 100.
  • Snakes move players down from a higher cell to a lower cell.
  • Ladders move players up from a lower cell to a higher cell.
  • Dice can have different rolling strategies (e.g., normal, loaded).
  • Players take turns rolling the dice and moving accordingly.
  • Game ends when a player reaches cell 100 exactly.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Design the Board and Cell classes
Define classes to represent the Board and its Cells, including snakes and ladders as special cell types or attributes.
๐Ÿ’ก Hint
Use an abstract class or inheritance to represent different cell types since cells have shared state (position) but different behaviors (snake, ladder, normal).
Use an abstract class Cell with a position field and an abstract method getDestination(); SnakeCell and LadderCell extend Cell to hold destination position; interface cannot hold instance state like position in Java, so abstract class is preferred.
Mid: Uses inheritance for Cell hierarchy. Senior: Explains why abstract class is chosen over interface due to shared instance state (position) and polymorphic behavior.
โš ๏ธCommon Wrong Answer
Using interface Cell with default methods but no fields leads to inability to store position state, causing design inconsistency and runtime errors.
Step 2
Implement Dice with Strategy Pattern
Design the Dice class to support different rolling behaviors (normal, loaded).
๐Ÿ’ก Hint
Use a Dice interface with a roll() method and concrete classes implementing different rolling strategies.
Define a Dice interface with int roll(); NormalDice and LoadedDice implement Dice; interface is suitable here because no shared instance state beyond method contract is needed.
Mid: Identifies Strategy pattern for dice rolling. Senior: Explains why interface is suitable here (no shared instance state), and how polymorphism enables runtime swapping of dice behavior.
โš ๏ธCommon Wrong Answer
Using a single Dice class with if-else for roll type violates Open-Closed Principle and makes extension error-prone.
Step 3
Design Player class with thread-safe turn management
Define Player class and ensure that player turns are managed safely in a concurrent environment.
๐Ÿ’ก Hint
Use synchronized methods or locks to ensure atomic move operations and volatile or AtomicInteger for player position to prevent stale reads.
Player class with volatile int position; public synchronized void move(int steps) to atomically update position; volatile ensures visibility across threads; synchronized prevents race conditions during move.
Mid: Uses synchronized for atomic move. Senior: Explains exact thread interleaving where without synchronized two threads could read same position and cause invalid moves.
โš ๏ธCommon Wrong Answer
Not using volatile or synchronized leads to visibility issues and race conditions where player position updates are lost or inconsistent.
Step 4
Implement GameMaster to coordinate game flow
Design a GameMaster class to manage players, board, dice, and game state transitions.
๐Ÿ’ก Hint
Use composition to hold references to Board, Dice, and Players; synchronize turn progression to avoid concurrent state corruption.
GameMaster holds Board, Dice, List<Player> as composition; public synchronized void playTurn() to ensure one player moves at a time; volatile boolean gameOver to signal game end with visibility guarantees.
Mid: Uses composition and synchronized playTurn. Senior: Explains how volatile gameOver prevents stale reads in other threads checking game status.
โš ๏ธCommon Wrong Answer
Using unsynchronized playTurn allows multiple players to move simultaneously causing inconsistent game state.
Step 5
Extendability: Adding new cell types without modifying existing code
How would you design the system to add new cell types (e.g., teleporters) without modifying existing classes?
๐Ÿ’ก Hint
Use abstract class Cell and polymorphism; follow Open-Closed Principle; consider Factory pattern for cell creation.
Use Factory pattern for Cell creation to encapsulate instantiation; abstract class Cell allows new subclasses without modifying existing code; interface cannot hold shared state so abstract class preferred.
Mid: Identifies Factory pattern for extensibility. Senior: Explains how abstract class with protected constructor and factory method enables adding new cell types without breaking existing code.
โš ๏ธCommon Wrong Answer
Hardcoding cell creation in Board class violates Open-Closed Principle and requires modifying Board for new cell types.
๐Ÿ”Œ
API Design Challenge
Design an API for the Snake and Ladder game that allows clients to start a game, add players, roll dice, and get game state updates.
  • Consider RESTful endpoints for game actions like /start, /roll, /status.
  • Use WebSocket or Server-Sent Events for real-time player move notifications.
  • Design request and response payloads to include player positions and game status.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • abstract class Cell with position field and polymorphic behavior
  • Dice interface with multiple implementations for rolling strategies
  • Player class with volatile position and synchronized move method
  • GameMaster with synchronized turn management and volatile gameOver flag
  • Factory pattern for Board cell creation to support extensibility
Good to Have - 30%
  • Use of composition with clear lifecycle ownership
  • Explanation of concurrency issues with exact thread interleaving
  • Open-Closed Principle adherence in design
  • Use of enums for cell types or player states
  • Observer pattern for player state change notifications (optional)
Bonus - 10%
  • Support for customizable dice strategies at runtime
  • Thread-safe notification system for player moves
  • Extensible cell types beyond snakes and ladders
Using interface for Cell with instance state leads to inability to store position causing runtime errors, resulting in design inconsistency and runtime failures.
Single Dice class with if-else logic violates Open-Closed Principle, making the code error-prone and difficult to extend with new dice behaviors.
Not using volatile or synchronized in Player leads to race conditions and stale reads, causing lost or inconsistent player position updates.
Unsynchronized GameMaster playTurn allows concurrent moves, corrupting game state and causing invalid game progression.
Hardcoding cell creation in Board violates Open-Closed Principle and hinders extensibility, requiring code changes for every new cell type.
โœ…
Model Answer
โ–ผ Reveal

    • abstract class Cell with position field and abstract getDestination() method to represent board cells polymorphically.
    • SnakeCell and LadderCell extend Cell and override getDestination() to return snake tail or ladder top respectively.
    • Dice interface defines roll() method; NormalDice and LoadedDice implement Dice for different rolling behaviors.
    • Player class uses volatile int position and synchronized move(int steps) to ensure thread-safe position updates.
    • GameMaster composes Board, Dice, and Players; uses synchronized playTurn() and volatile boolean gameOver for concurrency control.
    • Factory pattern used for Board cell creation to encapsulate instantiation and support extensibility.
    • Use of volatile ensures visibility of shared mutable state across threads, preventing stale reads.
    • Synchronized methods provide atomicity and mutual exclusion to prevent race conditions during state updates.
    • Open-Closed Principle enforced by abstract Cell and Factory pattern allowing new cell types without modifying existing classes.
    • Composition relationships specify lifecycle ownership: GameMaster owns Board, Dice, and Players; Board owns Cells.

    Practice

    (1/5)
    1. In the ESPNcricinfo system, the Match class has a private constructor and a static synchronized getInstance() method to ensure only one instance per match ID exists. Which design pattern is applied here and why is this the correct use case?
    easy
    A. Singleton Pattern, because it ensures a single instance per match ID with thread safety.
    B. Factory Pattern, because it creates objects based on input parameters.
    C. Builder Pattern, because it separates construction and representation of Match objects.
    D. Prototype Pattern, because it clones existing Match instances to avoid new creation.

    Solution

    1. Step 1: Identify the pattern from constructor and getInstance()

      The private constructor prevents direct instantiation, and the static synchronized getInstance() ensures controlled, thread-safe instance creation.
    2. Step 2: Why Singleton fits this use case

      Singleton ensures only one Match instance per match ID exists, avoiding inconsistent state across threads.
    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      Private constructor + synchronized getInstance() -> Singleton pattern [check]
    Hint: Private constructor + synchronized getInstance() -> Singleton [check]
    Common Mistakes:
    2. A class has a private constructor and a static synchronized getInstance() method that returns the single instance. Which design pattern is applied and why is this the CORRECT use case?
    easy
    A. Factory Pattern, because it controls object creation centrally.
    B. Prototype Pattern, because it clones existing instances to avoid new creation.
    C. Builder Pattern, because it constructs complex objects step-by-step.
    D. Singleton Pattern, because it ensures only one instance with thread-safe lazy initialization.

    Solution

    1. Step 1: Identify the pattern from private constructor and static synchronized method

      The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single instance is created lazily and safely in multithreaded contexts.
    2. Step 2: Eliminate other creational patterns

      Factory controls creation but allows multiple instances; Builder separates construction steps; Prototype clones instances. None enforce single instance with thread safety.
    3. Final Answer:

      Option D -> Option D
    4. Quick Check:

      Private constructor + synchronized getInstance() -> thread-safe Singleton [check]
    Hint: Private constructor + synchronized getInstance() -> Singleton [check]
    Common Mistakes:
    3. In the Meeting Scheduler, the RoomBookingManager class has a bookRoom() method that checks if a room is available and then marks it booked. Without synchronizing bookRoom(), which exact sequence of events between Thread 1 and Thread 2 causes a double-booking race condition?
    medium
    A. Thread 1 reads availability as false; Thread 2 reads availability as true; both proceed to book.
    B. Thread 1 and Thread 2 both wait on a lock, causing deadlock and no booking.
    C. Thread 1 books room; Thread 2 reads availability after booking and cancels its booking.
    D. Thread 1 checks isAvailable() -> true, context switch before booking; Thread 2 checks isAvailable() -> true, both book simultaneously.

    Solution

    1. Step 1: Identify check-then-act gap

      Without synchronization, Thread 1 reads availability as true but is preempted before booking; Thread 2 also reads true and proceeds.
    2. Step 2: Both threads book simultaneously causing double-booking

      Because the availability check and booking are not atomic, both threads can book the same room concurrently.
    3. Final Answer:

      Option D -> Option D
    4. Quick Check:

      Unsynchronized check-then-act -> double-booking race [check]
    Hint: Check-then-act without sync -> double-booking race [check]
    Common Mistakes:
    4. To add a WeekendFeeStrategy for weekend pricing in the current booking system design, which minimal change respects the Open-Closed Principle (OCP)?
    medium
    A. Modify the existing FeeCalculator class to add weekend logic with if-else conditions.
    B. Create a new class implementing FeeStrategy interface for weekend fees and inject it without changing existing classes.
    C. Add a boolean flag isWeekend in Booking and handle fees there.
    D. Extend FeeCalculator with a subclass that overrides fee calculation for weekends.

    Solution

    1. Step 1: Identify OCP requirement

      OCP requires adding new functionality without modifying existing tested code.
    2. Step 2: Evaluate options

      Add a boolean flag isWeekend in Booking and handle fees there. adds weekend flag in Booking, which violates OCP by modifying Booking class. Create a new class implementing FeeStrategy interface for weekend fees and inject it without changing existing classes. creates a new FeeStrategy class, respecting OCP by extension. Modify the existing FeeCalculator class to add weekend logic with if-else conditions. and D modify existing classes, violating OCP.
    3. Final Answer:

      Option C -> Option C
    4. Quick Check:

      New FeeStrategy class -> extend without modifying existing code [check]
    Hint: New strategy class -> extend, don't modify [check]
    Common Mistakes:
    5. ElectricCar pieces require special ChargingSpot compatibility in the Jigsaw Puzzle System. What is the minimum design change to support ElectricCar and ChargingSpot without modifying existing classes?
    hard
    A. Add ELECTRIC_CAR to PieceType enum, CHARGING to SpotType enum, create ElectricCarPiece subclass, and update compatibility map externally.
    B. Modify existing Piece and Spot classes to include electric-specific fields and logic.
    C. Create a new ElectricCarPiece class implementing a new interface and modify Board to handle it specially.
    D. Add a god-class CompatibilityManager that hardcodes all compatibility rules including electric cars.

    Solution

    1. Step 1: Identify extensibility requirements

      New piece and spot types must be added without modifying existing classes to respect OCP.
    2. Step 2: Evaluate minimal change approach

      Adding new enum values and subclass for ElectricCarPiece plus updating compatibility map externally allows extension without modifying existing code.
    3. Final Answer:

      Option A -> Option A
    4. Quick Check:

      New enums and subclass extend system without changing existing classes [check]
    Hint: Add enums + subclass + external map update = minimal invasive extension [check]
    Common Mistakes:
    Recommended

    Ready to prove it?

    Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

    10 questions~5 mineasy hard