System Design
Design With Sid
system design

Designing a Snake and Ladder Game

Designing a Snake and Ladder Game

Design Snake and Ladders

Introduction

In this post, we will walk through the system design of the classic Snake and Ladder game implemented in Java. The game is a turn-based race to reach the final position on a board (position 100), where players roll a dice and move their token accordingly. Along the way, snakes and ladders either pull players back or help them advance.

The primary challenge is to structure the design in a way that simulates this randomness and the interactions between different entities (players, board, dice) while maintaining simplicity. We'll also ensure that the player interaction is clear, including dice rolls and player movements.

By the end of this post, you'll have a working understanding of the code structure and key components, which include:

  • Dice: A class that simulates rolling a 6-sided dice.
  • Board: A representation of the game board with snakes and ladders.
  • Player: Each player’s position is tracked as they navigate the board.
  • Game: The core logic that manages player turns, moves, and win conditions.

We'll walk through the project file by file, explaining the purpose and function of each one. Let's get started!

Step 1: Player Entity

The Player entity represents a single player in the Snake and Ladder game. Each player needs to track their position on the board and have a name for identification during gameplay. In addition, players will move based on dice rolls and be affected by snakes and ladders.

Key Responsibilities:

  • Name: Uniquely identifies the player.
  • Position: Keeps track of the player's current location on the board.
  • Move: Updates the player's position based on the dice roll and handles snakes and ladders if applicable.

In a multiplayer game, several Player objects will be created and passed into the game logic.

Here's how the Player class is implemented:

package entities;

public class Player {
    private String name;
    private int pos;
    // Constructor to initialize player with a name and set starting position to 0
    public Player(String name) {
        this.name = name;
        this.pos = 0;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getPos() {
        return pos;
    }
    public void setPos(int pos) {
        this.pos = pos;
    }
}

Step 2: Dice Entity

The Dice entity simulates the rolling of a standard 6-sided die. This class is crucial for determining how many spaces a player moves during their turn. The randomness of the dice roll adds an element of chance to the game, making it unpredictable and exciting.

Key Responsibilities:

  • Roll Dice: Generates a random number between 1 and 6 to simulate a dice roll.

The Dice class will have a method to perform the roll, which returns the result of the dice throw.

Here's how the Dice class is implemented:

package entities;

import java.util.Random;

public class Dice {
    private Random random;

    // Constructor to initialize Random object
    public Dice() {
        random = new Random();
    }

    // Method to roll the dice
    public int rollDice() {
        return random.nextInt(6) + 1; // Generates a random number between 1 and 6
    }
}

Step 3: Board Entity

The Board entity represents the playing field for the Snake and Ladder game. It contains the positions of snakes and ladders, allowing players to interact with these elements as they move through the game.

Key Responsibilities:

  • Snakes: Maps starting positions to ending positions for snakes. Landing on a snake pulls the player back.
  • Ladders: Maps starting positions to ending positions for ladders. Landing on a ladder boosts the player forward.

The Board class is constructed using two HashMap objects, one for snakes and one for ladders.

Here's how the Board class is implemented:

package entities;

import java.util.HashMap;

public class Board {
    private HashMap<Integer, Integer> snakes;
    private HashMap<Integer, Integer> ladders;

    // Constructor to initialize snakes and ladders
    public Board(HashMap<Integer, Integer> snakes, HashMap<Integer, Integer> ladders) {
        this.snakes = snakes;
        this.ladders = ladders;
    }

    // Getter for snakes
    public HashMap<Integer, Integer> getSnakes() {
        return snakes;
    }

    public void setSnakes(HashMap<Integer, Integer> snakes) {
        this.snakes = snakes;
    }

    // Getter for ladders
    public HashMap<Integer, Integer> getLadders() {
        return ladders;
    }

    public void setLadders(HashMap<Integer, Integer> ladders) {
        this.ladders = ladders;
    }
}

Step 4: Game Entity

The Game entity orchestrates the flow of the Snake and Ladder game, integrating the Board, Dice, and Player entities. It manages player turns, checks for winning conditions, and updates player positions based on dice rolls and interactions with snakes and ladders.

Key Responsibilities:

  • Manage Players: Keeps track of all players and whose turn it is.
  • Game Logic: Handles the rules for moving players, including rolling the dice, checking for snakes and ladders, and determining when a player wins.
  • Game State: Maintains the state of the game, including whether it is over.

The Game class encapsulates the entire game logic, ensuring that players interact with the board according to the rules.

Here's how the Game class is implemented:

package entities;

import java.util.List;

public class Game {
    private Board board;
    private Dice dice;
    private List<Player> players;
    private int currentPlayerIndex;
    private boolean isGameOver;

    // Constructor to initialize the game components
    public Game(Board board, Dice dice, List<Player> players) {
        this.board = board;
        this.dice = dice;
        this.players = players;
        this.currentPlayerIndex = 0;
        this.isGameOver = false;
    }
    
    // Check if the game is over
    public boolean isGameOver() {
        return isGameOver;
    }

    // Set the game over state
    public void setGameOver(boolean isGameOver) {
        this.isGameOver = isGameOver;
    }

    // Manage a player's turn
    public void playerTurn() {
        if (isGameOver) return;

        Player currentPlayer = players.get(currentPlayerIndex);
        int initialPosition = currentPlayer.getPos();
        int diceRoll = dice.rollDice();

        int newPos = initialPosition + diceRoll;

        // Check for snakes
        if (board.getSnakes().containsKey(newPos)) {
            newPos = board.getSnakes().get(newPos);
        }

        // Check for ladders
        if (board.getLadders().containsKey(newPos)) {
            newPos = board.getLadders().get(newPos);
        }

        // Prevent moving past position 100
        if (newPos > 100) newPos = initialPosition;

        currentPlayer.setPos(newPos);
        System.out.println(currentPlayer.getName() + " rolled a " + diceRoll + " and moved from " + initialPosition + " to " + newPos);
        
        // Check for a win
        if (newPos == 100) {
            System.out.println(currentPlayer.getName() + " wins the game!");
            isGameOver = true;
            return;
        }

        // Move to the next player
        currentPlayerIndex = (currentPlayerIndex + 1) % players.size();
    }
}

Step 5: Main Class

The Main class serves as the entry point for the Snake and Ladder game. It initializes the game components, sets up players, and starts the game loop, allowing players to take turns until one of them wins.

Key Responsibilities:

  • Initialize Game Components: Creates instances of the Board, Dice, and Player entities.
  • Game Loop: Manages the turns of the players, repeatedly invoking the player's turn method until the game is won.

Here's how the Main class is implemented:

import entities.Board;
import entities.Dice;
import entities.Game;
import entities.Player;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Create snakes and ladders positions
        HashMap<Integer, Integer> snakes = new HashMap<>();
        snakes.put(17, 7);
        snakes.put(54, 34);
        snakes.put(62, 19);
        snakes.put(64, 60);
        snakes.put(87, 24);
        snakes.put(93, 73);
        snakes.put(95, 75);
        snakes.put(98, 79);

        HashMap<Integer, Integer> ladders = new HashMap<>();
        ladders.put(3, 22);
        ladders.put(5, 8);
        ladders.put(11, 26);
        ladders.put(20, 29);
        ladders.put(27, 1);
        ladders.put(39, 45);
        ladders.put(50, 91);
        ladders.put(70, 92);

        // Initialize the board with snakes and ladders
        Board board = new Board(snakes, ladders);
        Dice dice = new Dice();

        // Create a list of players
        List<Player> players = new ArrayList<>();
        players.add(new Player("Alice"));
        players.add(new Player("Bob"));

        // Initialize the game
        Game game = new Game(board, dice, players);

        // Game loop
        while (!game.isGameOver()) {
            game.playerTurn();
        }
    }
}

Sample Game Output

During a session of the Snake and Ladder game, the following moves were recorded:

  • Alice rolled a 3 and moved from 0 to 22
  • Bob rolled a 5 and moved from 0 to 5
  • Alice rolled a 6 and moved from 22 to 28
  • Bob rolled a 1 and moved from 5 to 6
  • .... so on
  • Alice rolled a 2 and moved from 98 to 100
  • Alice wins the game!

Conclusion

In this blog post, we have designed and implemented a simple yet engaging Snake and Ladder game using Java.

Key Takeaways:

  • Modular Design: By breaking down the game into distinct entities (Player, Dice, Board, and Game), we created a modular and maintainable codebase. This structure allows for easy enhancements and debugging.
  • Game Logic: The game logic is encapsulated within the Game class, managing player turns, interactions with snakes and ladders, and determining when a player wins.
  • User Experience: The game provides a straightforward and enjoyable experience, where players can roll the dice and navigate the board while encountering snakes and ladders that influence their progress.

Future Enhancements:

  • Multiple Players: Expand the player setup to allow for more than two players.
  • Graphical User Interface (GUI): Implement a GUI for a more interactive experience.
  • Scoreboard: Add a scoring system to keep track of player scores across multiple games.
  • Customizable Rules: Allow players to customize the positions of snakes and ladders or modify game rules.

This Snake and Ladder game serves as a practical example of system design principles and Java programming concepts. By following the steps outlined in this blog, you can gain insights into building your own game systems and enhance your understanding of software design.

Thank you for reading! We hope this project inspires you to explore further into game development and system design. Happy coding!