Snakes and Ladders
Concept
My project is a digital adaptation of the classic board game, Snakes and Ladders, which holds a special place in my heart due to fond childhood memories of playing it with my sister. I created this game as a way to relive those memories with her whenever we are apart. I made this to play the game with her whenever I am back home.
Upon opening the game, players are presented with a visually appealing cover page that offers the option to access instructions or dive right into the game play. The game features a soothing soundtrack that helps to enhance concentration and enjoyment. Two players can participate, each with a randomly assigned token represented by an ellipse in a unique color. The game board is comprised of four snakes and ladders each, with the placement randomized for each playthrough. These snakes and ladders can either hinder or assist the players, respectively, in reaching the goal of 100. As a luck-based game, there are no guarantees, but players who land on ladders can quickly ascend to the finish line, while those who land on snakes must descend to their tails. Upon completion, players can easily restart the game by simply pressing any key, returning them to the cover page for another round of fun.
Code
new p5();// Initialize the p5.js library as it random function was not working without this // Define some constants for the game const rows = 10; const cols = 10; const cellSize = 60; // Define some global variables for game elements let boardImage; let startTime = 0; let startTimeSnake = millis()*2; let startTimeLadder = millis()*2; let diceResult; let dice1; let dice2; let dice3; let dice4; let dice5; let dice6; let cover; let back; let restart; let instructions; let bg_music; let dice_sound; // Define ladders const ladders = [ { start: 2, end: 22}, { start: 16, end: 37}, { start: 36, end: 74}, { start: 58, end: 85} ]; // Define snakes const snakes = [ { start: 99, end: 1 }, { start: 90, end: 61 }, { start: 50, end: 23 }, { start: 39, end: 17 } ]; // Preload all the game assets function preload() { boardImage = loadImage("boardImage.png"); dice1 = loadImage("1.png"); dice2 = loadImage("2.png"); dice3 = loadImage("3.png"); dice4 = loadImage("4.png"); dice5 = loadImage("5.png"); dice6 = loadImage("6.png"); cover = loadImage("cover.png"); back = loadImage("back.png"); instructions = loadImage("instructions.png"); restart = loadImage("restart.png"); bg_music = loadSound("bgmusic.mp3"); } // Set up the canvas function setup() { createCanvas(cellSize * cols, cellSize * rows); } function draw() { // Start playing background music if it's not already playing if (!bg_music.isPlaying()) { bg_music.play(); } board.display(); //handling mouse click events handledMouseEvent = false; if ((millis() - startTime < 2000) && board.gameState == "start") { diceDisplay(); } if ((millis() - startTimeSnake < 2000) && board.gameState == "start" && frameCount > 180) { board.snakeText(); } if ((millis() - startTimeLadder < 2000) && board.gameState == "start" && frameCount > 180) { board.ladderText(); } } // Roll the dice and return the result function getDiceRoll() { startTime = millis(); diceResult = floor(random(1, 7)); return diceResult; } // Display the dice image based on the result out of the 6 images function diceDisplay() { let diceWidth = 100; let diceHeight = 100; let diceX = 250; let diceY = 250; if (diceResult == 1) { dice1.resize(diceWidth, diceHeight); image(dice1, diceX, diceY); } else if (diceResult == 2) { dice2.resize(diceWidth, diceHeight); image(dice2, diceX, diceY); } else if (diceResult == 3) { dice3.resize(diceWidth, diceHeight); image(dice3, diceX, diceY); } else if (diceResult == 4) { dice4.resize(diceWidth, diceHeight); image(dice4, diceX, diceY); } else if (diceResult == 5) { dice5.resize(diceWidth, diceHeight); image(dice5, diceX, diceY); } else if (diceResult == 6) { dice6.resize(diceWidth, diceHeight); image(dice6, diceX, diceY); } } class Board { constructor() //initializes various game-related variables, including the player array and the game state { startTime = 0; this.playerArray = []; this.playerNum = 2; this.counter = 0; this.playerIndex = 0; this.encounter = false; this.gameState = "cover"; for (let i = 0; i < this.playerNum; i++) { this.playerArray[i] = new Player(); } } display() //displays different images depending on the game state, including the game board, cover page, instructions, and restart page. { if (this.gameState == "cover") { image(cover, 0, 0); if (mouseX >= 170 && mouseX <= 405 && mouseY >= 385 && mouseY <= 460) { noStroke(); fill(255, 0, 0, 100); rect(170, 385, 235, 80); } if (mouseX >= 20 && mouseX <= 580 && mouseY >= 490 && mouseY <= 570) { noStroke(); fill(255, 0, 0, 100); rect(20, 488, 560, 83); } } else if (this.gameState == "start") { image(boardImage, 0, 0); for (let i = 0; i < this.playerNum; i++) { this.playerArray[i].display(); } } else if (this.gameState == "instructions") { image(instructions, 0, 0); image(back, 10, 10); } else if (this.gameState == "restart") { image(restart, 0, 0); } } //called whenever it is the player's turn to move, and it moves the player based on the number they rolled on the dice playing() { this.playerIndex = this.counter % this.playerNum; this.playerArray[this.playerIndex].movement(getDiceRoll()); let newPos = this.checkForSnakeAndLadder(this.playerArray[this.playerIndex].player_position); let oldPos = this.playerArray[this.playerIndex].player_position; let movingPos = newPos - oldPos; this.playerArray[this.playerIndex].movement(movingPos); this.counter = this.counter + 1; this.checkForWin(); } //method checks whether any player has reached the end of the board and sets the game state to restart if so. checkForWin() { for (let i = 0; i < this.playerNum; i++) { if (this.playerArray[i].player_position > 99) { this.gameState = "restart"; } } } //checks whether the player has landed on a snake or ladder and returns the new position accordingly checkForSnakeAndLadder(position) { for (let ladder of ladders) { if (ladder.start == position) { startTimeLadder = millis(); return ladder.end; } } for (let snake of snakes) { if (snake.start == position) { startTimeSnake = millis(); return snake.end; } } return position; } snakeText() { textSize(30); fill(0, 0, 0); rect(200, 400, 200, 50); fill(255, 255, 255); text("SNAKE!", 240, 435); } ladderText() { textSize(30); fill(0, 0, 0); rect(200, 400, 200, 50); fill(255, 255, 255); text("LADDER", 240, 435); } } class Player { constructor(player_color) //initializes the player's position and color, and the movement method moves the player on the board { this.player_position = 0; this.player_color = player_color; this.player_x = cellSize/2; this.player_y = cellSize/2; this.red = random(255); this.green = random(255); this.blue = random(255); this.opacity = 225; } movement(dice_roll) // movement dependent on dice roll as an arguement { this.player_position = this.player_position + dice_roll; this.player_x = (this.player_position % cols) * cellSize + (cellSize/2); this.player_y = floor(this.player_position / cols) * cellSize + (cellSize/2); } display() //displays the player's avatar on the board. { fill(this.red, this.green, this.blue, this.opacity); ellipse(this.player_x, this.player_y, cellSize/2, cellSize/2); } } let board = new Board(); // Use a flag to indicate if the mouse click event has been handled let handledMouseEvent = false; function mouseClicked() // handles mouse clicks during the game, allowing the player to roll the dice and move accordingly { if (board.gameState == "start") { // Check if the event has already been handled if (!handledMouseEvent) { board.playing(); handledMouseEvent = true; } } else if (board.gameState == "cover") { handledMouseEvent = true; if (mouseX >= 170 && mouseX <= 405 && mouseY >= 385 && mouseY <= 460) { board.gameState = "start"; } if (mouseX >= 20 && mouseX <= 580 && mouseY >= 490 && mouseY <= 570) { handledMouseEvent = true; board.gameState = "instructions"; } } else if (board.gameState == "instructions") { if (mouseX >= 30 && mouseX <= 90 && mouseY >= 30 && mouseY <= 90) { board.gameState = "cover"; } } } function keyPressed() //allows the player to restart the game if they win. { if (board.gameState == "restart") { board = new Board(); } } remove();
Overview
The game begins with a cover page that offers the player two options: to start playing or to read the instructions. Once the player begins the game, they roll a dice to determine the number of moves they can make on the board. The board is a 10×10 grid that features both snakes and ladders in specific cells. If the player lands on a ladder’s bottom cell, they climb up to the ladder’s top cell. If they land on a snake’s head cell, they slide down to the snake’s tail cell. Before the game begins, the program initializes various constants, global variables, ladders, and snakes. It also preloads all the game assets, sets up the canvas, and defines several functions to handle mouse clicks, retrieve the dice roll value, display the corresponding dice image, and show different images based on the game state. The game employs a Board class, which initializes various game-related variables, such as the player array and the game state. The class offers methods to display different images based on the game state, update the player’s position on the board, and display text when the player lands on a snake or ladder cell.
The cover page is the first thing that the player sees, and it sets the tone for the entire game. The font and background theme of the snake design create a sense of excitement and anticipation, drawing the player into the game before it even begins. Creating the board game that you played as a child was a significant accomplishment that speaks to your creativity and resourcefulness. It likely required a lot of time and effort to design and produce, but the end result is something that you can be truly proud of.
Future Improvements
To add more interactive features to the game, there are a few different directions that could be explored. One option is to introduce different levels of difficulty, which could be achieved by changing the layout of the board or adding new challenges for the player to overcome. For example, some cells on the board could be marked as “obstacle” cells, where the player has to complete a mini-game or answer a trivia question to progress.
Another way to make the game more engaging is to introduce power-ups or special abilities that the player can use to gain an advantage. For instance, the player could collect “snake repellent” items that allow them to skip over snake cells or “ladder boosters” that let them climb higher on ladder cells. These power-ups could be randomly placed on the board or awarded to the player for completing certain challenges.
Moreover, bonus points or rewards could be given to the player for landing on specific cells on the board. For example, landing on a cell with a picture of a golden apple could give the player bonus points, while landing on a cell with a picture of a bomb could subtract points from their score. This would add an element of strategy to the game, as players would have to decide whether to take risks and aim for high-reward cells or play it safe and avoid the high-risk ones. In terms of graphics and user interface, there are many ways to enhance the visual appeal and immersion of the game. For example, the background could be animated to show a moving snake or ladders climbing up and down the sides of the board. The game could also have different themes or environments, such as a jungle or a castle, with corresponding graphics and sound effects. This for me was very hard to try implementing but I still tried and could not.