Features Required:
Board Initialization: A board with a predefined size.
Players: Multiple players can play the game.
Snakes and Ladders: Random placement of snakes and ladders.
Dice Roll: A player can roll a dice and move accordingly.
Game Logic: Movement according to dice roll and handling snakes and ladders.
Design Patterns Involved:
Singleton Pattern: Used for creating a single instance of the board to ensure that all players interact with the same board.
Factory Pattern: Used to create snakes and ladders on the board.
Observer Pattern: Used to notify players about their turns.
Strategy Pattern: Used to define the dice rolling strategy, which can be changed dynamically.
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();
}
}
}
Issues in the Above Design (Covered in our premium course)
Single-threaded Design:
Issue: The current design is single-threaded, which may not be suitable for real-time multiplayer games.
Resolution: Introduce multithreading to handle simultaneous player actions and improve performance.
Scalability Issues:
Issue: The design may not scale well with a large number of players or an extensive board size.
Resolution: Optimize data structures and algorithms to handle larger scales efficiently.
No Separation of Concerns:
Issue: The design mixes game logic, user notifications, and command execution.
Resolution: Refactor the code to separate these concerns, adhering to the Single Responsibility Principle (SRP).
Limited Extensibility:
- Issue: Adding new features or modifying existing ones may require significant code changes.
Performance Bottlenecks:
Issue: Random number generation and frequent lookups in maps may cause performance issues.
Resolution: Optimize these operations and consider more efficient data structures if necessary.
Extensions of the Design
Customizable Board:
Description: Allow users to create and play on custom boards.
Implementation: Implement a configuration file or a GUI-based board editor where users can define the board size, and the positions of snakes and ladders. - Covered in ourpremium course.
**Support Multi-Board -Covered in ourpremium course.
Save and Load Game State:
Description: Provide functionality to save and load the game's state.
Implementation: Covered in ourpremium course.
AI Players:
Description: Introduce computer-controlled players with varying difficulty levels.
Implementation: Implement AI strategies for making moves and integrate them into the game logic. Allow users to choose between human and AI players - Covered in ourpremium course.
Different Dice Types:
Description: Offer various types of dice with different numbers of faces.
Implementation: Modify the
DiceStrategy
to include different dice types. Allow players to select their preferred dice type before starting the game - Covered in ourpremium course.
Enhanced Game Rules:
Description: Add optional and customizable game rules.
Implementation: Implement additional rules such as extra turns for rolling doubles or penalties for specific squares. Allow players to configure these rules at the start of the game - Covered in ourpremium course.
Player Statistics and Leaderboards:
Description: Track player performance and maintain leaderboards.
Implementation: Store player statistics such as wins, losses, and total games played. Display leaderboards showing top players based on these statistics - Covered in ourpremium course.
Playground (test/run above code) - https://ide.lldcoding.com/snake-and-ladder-game
Soon will add YouTube video on this channel - https://www.youtube.com/channel/UCgdIgkU_hq0VtGPhVPA0CPQ