AlgoMaster Logo

Design Snake and Ladder game

Last Updated: January 22, 2026

Ashish

Ashish Pratap Singh

easy

In this chapter, we will explore the low-level design of a snake and ladder game in detail.

Lets start by clarifying the requirements:

1. Clarifying Requirements

Before starting the design, it's important to ask thoughtful questions to uncover hidden assumptions and better define the scope of the system.

Here is an example of how a conversation between the candidate and the interviewer might unfold:

After gathering the details, we can summarize the key system requirements.

1.1 Functional Requirements

  • The game is played on a standard 10x10 board with 100 numbered cells
  • Support configuration of snakes and ladders with flexible start and end positions
  • Snakes send the player back to a lower-numbered square; ladders move the player forward
  • Allow multiple players (minimum two), with turn rotation in round-robin order
  • Simulate dice rolls with random values between 1 and 6. A player gets an extra turn if they roll a 6
  • A player must roll the exact number to land on cell 100 and win the game
  • Multiple players can occupy the same cell without interaction

1.2 Non-Functional Requirements

  • Modularity: The system should follow object-oriented principles with clean separation between components
  • Extensibility: The design should allow future enhancements such as custom board sizes or different types of dice
  • Maintainability: The codebase should be clean, readable, and easy to extend
  • User Feedback: The system should provide clear console output after each turn, indicating player moves, dice rolls, snake or ladder interactions, and current positions

After the requirements are clear, lets identify the core entities/objects we will have in our system.

2. Identifying Core Entities

How do you go from a list of requirements to actual classes? The key is to look for nouns in the requirements that have distinct attributes or behaviors. Not every noun becomes a class, but this approach gives you a starting point.

Let's walk through our requirements and identify what needs to exist in our system.

1. The game is played on a standard 10x10 board with 100 numbered cells.

The board is central to everything. We need something to represent it. This gives us our first entity: Board.

Unlike Tic-Tac-Toe where each cell holds a symbol, Snake and Ladder cells are just positions. The interesting part is what happens at certain positions. The Board needs to know which positions have snakes or ladders and where they lead.

2. Support configuration of snakes and ladders with flexible start and end positions

We clearly need Snake and Ladder entities. Both have a start position and an end position. A snake's start (head) must be higher than its end (tail). A ladder's start (bottom) must be lower than its end (top).

Notice the similarity?

Both are "board entities" that transport a player from one position to another. This suggests an abstract base class BoardEntity that Snake and Ladder can inherit from. The base class holds the common logic (start and end positions), while subclasses enforce their specific validation rules.

3. Allow multiple players (minimum two), with turn rotation in round-robin order

We need to represent players. Each player has a name and a current position on the board. This gives us the Player entity.

Players start at position 0 (off the board) and move toward 100. Their position changes after each dice roll, potentially modified by snakes or ladders.

4. Simulate dice rolls with random values between 1 and 6

We need a Dice entity to simulate random rolls. While we could just call Math.random() directly in the game logic, encapsulating dice behavior in its own class provides several benefits:

  • We can easily swap in different dice types (weighted, multiple dice)
  • We can mock the dice for testing
  • The dice range becomes configurable

5. The game should manage turns, apply snakes and ladders, and determine when a player wins

Something needs to coordinate the gameplay: roll dice, move players, check for snakes/ladders, handle the special "roll 6 for extra turn" rule, and detect when someone wins. This orchestrator is our Game entity.

The game also needs to track its current state. Is it still running? Has someone won? An enum GameStatus with values NOT_STARTEDRUNNING, and FINISHED captures all possibilities cleanly.

Entity Overview

Here's how these entities relate to each other:

We've identified three types of entities:

Enums define fixed sets of values. GameStatus tracks the game lifecycle.

Data Classes primarily hold data with minimal behavior. Player tracks name and position. BoardEntity (and its subclasses) holds start and end positions.

Core Classes contain the main logic. Dice generates random rolls, Board manages position transitions, and Game orchestrates the entire gameplay loop.

Scroll
EntityTypeResponsibility
GameStatusEnumGame lifecycle: NOT_STARTED, RUNNING, FINISHED
PlayerData ClassHolds player name and current position
BoardEntityAbstract ClassBase class for snakes and ladders with start/end positions
SnakeData ClassBoard entity where start > end (sends player backward)
LadderData ClassBoard entity where start < end (moves player forward)
DiceCore ClassSimulates random dice rolls within a range
BoardCore ClassManages the board and position transitions
GameCore ClassOrchestrates gameplay, turns, and win detection

With our entities identified, let's define their attributes, behaviors, and relationships.

3. Class Design

Now that we know what entities we need, let's flesh out their details. For each class, we'll define what data it holds (attributes) and what it can do (methods). Then we'll look at how these classes connect to each other.

3.1 Class Definitions

We'll work bottom-up: simple types first, then data containers, then the classes with real logic. This order makes sense because complex classes depend on simpler ones.

Enums

Enums define fixed sets of values that provide type safety and make code self-documenting.

GameStatus

Tracks where we are in the game lifecycle.

Scroll
ValueDescriptionTerminal?
NOT_STARTEDGame is initialized but not yet startedNo
RUNNINGPlayers are actively taking turnsNo
FINISHEDA player has won the gameYes

Three distinct states cover the game lifecycle. Unlike Tic-Tac-Toe, there's no DRAW state because Snake and Ladder always eventually produces a winner.

Data Classes

Data classes are simple containers that hold data with minimal behavior.

Player

Encapsulates all relevant information about a player

Scroll
AttributeTypeDescriptionMutable?
nameStringPlayer identifier (e.g., "Alice")No
positionintCurrent position on the board (0-100)Yes
MethodDescription
Player(name)Constructor, initializes position to 0
getName()Returns the player's name
getPosition()Returns current position
setPosition(position)Updates the player's position

The Player starts at position 0, which represents "off the board" (before cell 1). The position is mutable because it changes after every move. The name is immutable since it never changes during gameplxxay.

BoardEntity

Abstract base class for snakes and ladders.

Scroll
AttributeTypeDescription
startintPosition where the entity begins
endintPosition where the entity transports the player
MethodDescription
BoardEntity(start, end)Constructor storing start and end
getStart()Returns the start position
getEnd()Returns the end position

Snake

Represents a snake on the board.

ValidationRule
ConstructorThrows exception if start <= end

When a player lands on the snake's head (start position), they slide down to the tail (end position). The constructor enforces that the head is always higher than the tail.

Ladder

Represents a ladder on the board.

ValidationRule
ConstructorThrows exception if start >= end

When a player lands on the ladder's bottom (start position), they climb up to the top (end position). The constructor enforces that the bottom is always lower than the top.

Core Classes

Core classes contain the actual game logic.

Dice

A utility class responsible for simulating a dice roll.

Scroll
AttributeTypeDescription
minValueintMinimum roll value (typically 1)
maxValueintMaximum roll value (typically 6)
MethodDescription
Dice(minValue, maxValue)Constructor with configurable range
roll()Returns random value between min and max (inclusive)

Board

Manages the game board and position transitions.

Scroll
AttributeTypeDescription
sizeintTotal number of cells (100 for standard board)
snakesAndLaddersMap<Integer, Integer>Maps start positions to end positions
MethodDescription
Board(size, entities)Constructor that builds the position map from entities
getSize()Returns the board size
getFinalPosition(position)Returns the final position after applying any snake/ladder

The Board doesn't store individual cells because we don't need to track cell contents. Instead, it maintains a map from snake/ladder start positions to their end positions. When a player lands on a position, we check if there's an entry in this map. If so, we return the mapped position; otherwise, we return the original position.

Game

Main orchestrator that coordinates all game elements.

Scroll
AttributeTypeDescription
boardBoardThe game board (composition)
playersQueue<Player>Players in turn order
diceDiceThe dice for rolling (composition)
statusGameStatusCurrent game state
winnerPlayerThe winning player (null until game ends)
MethodDescription
Game(Builder)Private constructor, takes Builder
play()Main game loop that runs until someone wins
takeTurn(player)Handles a single player's turn

Key Design Principles:

  1. Orchestration: Game ties everything together. It owns the Board and Dice, manages the Player queue, and runs the game loop until a winner is determined.
  2. Queue for Turn Management: Using a Queue for turn rotation is natural. We poll the front player for their turn, and if the game continues, add them back to the queue. This handles any number of players elegantly.
  3. Builder Pattern: Game construction is complex (board, players, dice all need configuration). The Builder pattern makes this clean and readable.

3.2 Class Relationships

How do these classes connect? Let's examine the relationship types we use.

Composition (Strong Ownership)

Composition means one object owns another. When the owner is destroyed, the owned object is destroyed too.

  • Game owns Board: When you create a Game, it creates its own Board. The Board exists only for that game.
  • Game owns Dice: Each Game creates its own Dice. The Dice doesn't exist outside the game context.

Aggregation (Weak Ownership)

Aggregation means one object contains other objects, but the contained objects can exist independently.

  • Game manages Players: The Game has a collection of Players. Players are conceptually separate entities that could exist outside this specific game (imagine a player playing multiple games).
  • Board aggregates BoardEntities: The Board uses a list of BoardEntity objects (snakes and ladders) that are created outside the Board and passed to it.

Inheritance (Is-A)

Inheritance defines a hierarchy between classes where subclasses are specialized versions of the parent.

  • Snake extends BoardEntity: A Snake is a specialized BoardEntity that transports players downward.
  • Ladder extends BoardEntity: A Ladder is a specialized BoardEntity that transports players upward.

3.3 Key Design Patterns

You might notice some structural patterns emerging in our design. Let's make them explicit and justify why each pattern is appropriate here.

Builder Pattern

The Problem: Creating a Game requires multiple configuration steps: setting up the board with snakes and ladders, adding players, and configuring the dice. If we use a constructor with many parameters, it becomes hard to read and error-prone. What order do the parameters go in? Which ones are optional?

The Solution: The Builder pattern encapsulates the construction logic in a separate Builder class. Each configuration step returns the builder, allowing method chaining. The final build() call validates everything and creates the Game.

Why This Pattern: We could use a constructor with many parameters, but consider:

Template Method Pattern (BoardEntity Hierarchy)

The Problem: Snakes and ladders share the same structure (start and end positions) but have different validation rules. We don't want to duplicate the common code.

The Solution: The abstract BoardEntity class defines the common structure and behavior. Subclasses (SnakeLadder) provide their specific validation in their constructors, which call the parent constructor after validation.

Why This Pattern: This is a classic use of inheritance for shared structure with specialized behavior:

  • Common code (storing start/end, getters) lives in the base class
  • Validation rules specific to each type live in subclass constructors
  • Adding a new board entity type (e.g., a "Portal" that teleports to a random position) just requires a new subclass

Facade Pattern (Game as Controller)

The Problem: External code shouldn't need to understand the internals of Board, Dice, and Player management. They just want to start and play a game.

The Solution: The Game class acts as a facade, providing simple play() method that hides all the complexity of turn management, dice rolling, position updates, and win detection.

Why This Pattern: The Game class provides:

  • A single entry point for running the game
  • Encapsulation of the game loop logic
  • Console output for game progress without requiring external coordination

3.4 Full Class Diagram

4. Code Implementation

Now let's translate our design into working code. We'll build bottom-up: foundational types first, then data classes, then the classes with real logic. This order matters because each layer depends on the ones below it.

4.1 Enum

We start with the enum that tracks game state.

Three states cover the game lifecycle. The game starts NOT_STARTED, transitions to RUNNING when play begins, and ends as FINISHED when someone wins.

4.2 Data Classes

These classes primarily hold data with minimal logic.

Players start at position 0, which represents "off the board" (before cell 1). The name is immutable, but position changes throughout the game.

Now let's implement the BoardEntity hierarchy:

The abstract base class stores the common attributes. Both fields are final because a snake or ladder's position never changes during the game.

The Snake constructor enforces the rule that snakes go downward. If someone tries to create a snake where the head is below the tail, we fail immediately with a clear error message.

Similarly, the Ladder constructor enforces that ladders go upward. This "fail fast" validation catches configuration errors immediately.

4.3 Dice Class

The Dice encapsulates random number generation with a configurable range.

The formula Math.random() * (max - min + 1) + min generates a random integer in the inclusive range [min, max]. For a standard die, this returns values 1 through 6 with equal probability.

4.4 Board Class

The Board manages the game surface and position transitions.

The Board converts the list of BoardEntity objects into a Map for O(1) position lookups. The getFinalPosition() method is elegant: if the position is in the map (snake head or ladder bottom), return the mapped value. Otherwise, return the original position. This single method handles both snakes and ladders uniformly.

4.5 Game Class

This is where everything comes together. The Game orchestrates the entire gameplay loop.

Let's break down the key aspects of the Game class:

The play() method:

  1. Validates minimum player count
  2. Sets status to RUNNING
  3. Loops until someone wins, using a Queue for turn rotation
  4. After each turn, if the game continues, the player goes back to the end of the queue

The takeTurn() method handles all the game rules:

  1. Roll the dice and print the result
  2. Calculate the next position
  3. If overshooting 100, skip the turn
  4. If landing exactly on 100, declare winner
  5. Check for snakes/ladders and print appropriate message
  6. Update player position
  7. If rolled a 6, recursively call takeTurn() for the extra turn

The Builder pattern:

  • Private Game constructor forces use of Builder
  • Each setter returns this for method chaining
  • build() validates all components are set
  • Clean, readable game construction

Game Flow Sequence

The following diagram illustrates what happens during a player's turn:

4.6 Demo Class

Let's see the system in action with a demo that sets up a game with snakes and ladders.

The demo creates:

  • 4 snakes at positions 17→7, 54→34, 62→19, and 98→79
  • 4 ladders at positions 3→38, 24→33, 42→93, and 72→84
  • 3 players: Alice, Bob, and Charlie
  • A standard 1-6 dice

The Builder pattern makes this setup clean and readable. Each configuration step is explicit.

5. Run and Test

Loading editor...

6. Quiz

Design Snake and Ladder Game Quiz

1 / 20
Multiple Choice

In the object-oriented design of a Snake and Ladder game, which entity is responsible for storing the positions of all snakes and ladders?