Loading...

12-Hour Money-Back Guarantee

Design (LLD) Snake and Ladder Game - Machine Coding

Design (LLD) Snake and Ladder Game - Machine Coding

Design (LLD) Snake and Ladder Game - Machine Coding

26 Jun 20246 min read

Features Required:

  1. Board Initialization: A board with a predefined size.

  2. Players: Multiple players can play the game.

  3. Snakes and Ladders: Random placement of snakes and ladders.

  4. Dice Roll: A player can roll a dice and move accordingly.

  5. Game Logic: Movement according to dice roll and handling snakes and ladders.

Design Patterns Involved:

  1. Singleton Pattern: Used for creating a single instance of the board to ensure that all players interact with the same board.

  2. Factory Pattern: Used to create snakes and ladders on the board.

  3. Observer Pattern: Used to notify players about their turns.

  4. Strategy Pattern: Used to define the dice rolling strategy, which can be changed dynamically.

  5. Command Pattern: Used to encapsulate a request as an object to parameterize clients with queues, requests, and operations.

Detailed Implementation

Singleton Pattern for Board

public class Board {
    private static Board instance;
    private int size;
    private Map<Integer, Integer> snakes;
    private Map<Integer, Integer> ladders;

    private Board(int size) {
        this.size = size;
        this.snakes = new HashMap<>();
        this.ladders = new HashMap<>();
    }

    public static Board getInstance(int size) {
        if (instance == null) {
            instance = new Board(size);
        }
        return instance;
    }

    public int getSize() {
        return size;
    }

    public Map<Integer, Integer> getSnakes() {
        return snakes;
    }

    public Map<Integer, Integer> getLadders() {
        return ladders;
    }

    public void addSnake(int start, int end) {
        snakes.put(start, end);
    }

    public void addLadder(int start, int end) {
        ladders.put(start, end);
    }
}

Factory Pattern for Creating Snakes and Ladders

public class ObstacleFactory {
    public static void createSnakes(Board board, List<int[]> snakes) {
        for (int[] snake : snakes) {
            board.addSnake(snake[0], snake[1]);
        }
    }

    public static void createLadders(Board board, List<int[]> ladders) {
        for (int[] ladder : ladders) {
            board.addLadder(ladder[0], ladder[1]);
        }
    }
}

Observer Pattern for Player Turns

public interface Observer {
    void update(String message);
}

public class Player implements Observer {
    private String name;
    private int position;

    public Player(String name) {
        this.name = name;
        this.position = 0;
    }

    public String getName() {
        return name;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }

    @Override
    public void update(String message) {
        System.out.println(name + ": " + message);
    }
}

public class Game {
    private List<Player> players;
    private int currentPlayerIndex;

    public Game() {
        this.players = new ArrayList<>();
        this.currentPlayerIndex = 0;
    }

    public void addPlayer(Player player) {
        players.add(player);
    }

    public void notifyPlayers(String message) {
        for (Player player : players) {
            player.update(message);
        }
    }

    public Player getCurrentPlayer() {
        return players.get(currentPlayerIndex);
    }

    public void nextTurn() {
        currentPlayerIndex = (currentPlayerIndex + 1) % players.size();
    }
}

Strategy Pattern for Dice Roll

public interface DiceStrategy {
    int rollDice();
}

public class NormalDice implements DiceStrategy {
    private Random random;

    public NormalDice() {
        this.random = new Random();
    }

    @Override
    public int rollDice() {
        return random.nextInt(6) + 1;
    }
}

// The Loaded Dice is an example of a biased dice that doesn't generate numbers randomly. 
// Instead, it skews the probabilities, typically favoring higher numbers, making it "loaded" to produce certain outcomes more frequently.
public class LoadedDice implements DiceStrategy {
    private Random random;

    public LoadedDice() {
        this.random = new Random();
    }

    @Override
    public int rollDice() {
        return random.nextInt(3) + 4;  // Rolls between 4 and 6
    }
}

Command Pattern for Game Moves

public interface Command {
    void execute();
}

public class MoveCommand implements Command {
    private Player player;
    private int steps;
    private Board board;

    public MoveCommand(Player player, int steps, Board board) {
        this.player = player;
        this.steps = steps;
        this.board = board;
    }

    @Override
    public void execute() {
        int newPosition = player.getPosition() + steps;
        if (newPosition > board.getSize()) {
            newPosition = board.getSize();
        }
        if (board.getSnakes().containsKey(newPosition)) {
            newPosition = board.getSnakes().get(newPosition);
        } else if (board.getLadders().containsKey(newPosition)) {
            newPosition = board.getLadders().get(newPosition);
        }
        player.setPosition(newPosition);
    }
}

Main Game Class

public class SnakeAndLadderGame {
    public static void main(String[] args) {
        Board board = Board.getInstance(100);
        ObstacleFactory.createSnakes(board, Arrays.asList(new int[][]{{16, 6}, {48, 26}, {49, 11}, {56, 53}, {62, 19}, {64, 60}, {87, 24}, {93, 73}, {95, 75}, {98, 78}}));
        ObstacleFactory.createLadders(board, Arrays.asList(new int[][]{{1, 38}, {4, 14}, {9, 31}, {21, 42}, {28, 84}, {36, 44}, {51, 67}, {71, 91}, {80, 100}}));

        Game game = new Game();
        Player player1 = new Player("Alice");
        Player player2 = new Player("Bob");

        game.addPlayer(player1);
        game.addPlayer(player2);

        DiceStrategy dice = new NormalDice();

        while (true) {
            Player currentPlayer = game.getCurrentPlayer();
            int diceRoll = dice.rollDice();
            Command moveCommand = new MoveCommand(currentPlayer, diceRoll, board);
            moveCommand.execute();

            game.notifyPlayers(currentPlayer.getName() + " rolled a " + diceRoll + " and moved to " + currentPlayer.getPosition());

            if (currentPlayer.getPosition() == board.getSize()) {
                game.notifyPlayers(currentPlayer.getName() + " wins!");
                break;
            }

            game.nextTurn();
        }
    }
}