Rock, Paper, Scissor, Lizard, Spock game in javascript.

Learn how to create the rock, paper, scissor, lizard, spock game in javascript.

Rock, Paper, Scissor, Lizard, Spock game in javascript

It is the modified version of classic rock, paper, scissor game with two extra options which highly reduces the probability of choosing same options by both the players.

By building this game you will learn

  • HTML:- How to do layouting so that you can select and update the elements.
  • CSS:- Style elements properly and reuse the code.
  • Javascript: Create elements dynamically, add and remove dynamic elements, create a bot to compete with.

As this is a little complex project, I will sum up the article by breaking down into in three different parts.

  1. First, we will see how to structure the elements.
  2. Second, style the elements.
  3. Third, handle the functionality with Javascript.

Creating HTML layout of rock, paper, scissor, lizard, Spock game.

If you see the above image carefully, there are basically three sections in which we can divide the layout.

One, where the score and message regarding who won the round will be shown.

<!-- Result -->
<section id="result">
    <div id="score">
        <span id="player1-score" class="points">0</span>
        :
        <span id="player2-score" class="points">0</span>
    </div>
    <p id="round-message">Choose your option</p>
</section>

Two, where the selected option and all available options will be visible for both the players.


<!-- Player 1 area -->
<section id="player1" class="players">
    <span class="name">Player1</span>
    <div class="selected-option">
        <span class="option" data-index="0"> </span>
    </div>
    <div class="available-options">
        <span class="option" data-index="0">
            <img src="assets/lizard.png" alt="Lizard" title="Lizard" />
        </span>
        <span class="option" data-index="1">
            <img src="assets/paper.png" alt="Paper" title="Paper" />
        </span>
        <span class="option" data-index="2">
            <img src="assets/rock.png" alt="Rock" title="Rock" />
        </span>
        <span class="option" data-index="3">
            <img src="assets/scissor.png" alt="Scissor" title="Scissor" />
        </span>
        <span class="option" data-index="4">
            <img src="assets/spock.png" alt="Spock" title="Spock" />
        </span>
    </div>
</section>

<!-- Player 2 area -->
<section id="player2" class="players">
    <span class="name">Bot</span>
    <div class="selected-option">
        <span class="option" data-index="0"> </span>
    </div>
    <div class="available-options">
        <span class="option" data-index="0">
            <img src="assets/lizard.png" alt="Lizard" title="Lizard" />
        </span>
        <span class="option" data-index="1">
            <img src="assets/paper.png" alt="Paper" title="Paper" />
        </span>
        <span class="option" data-index="2">
            <img src="assets/rock.png" alt="Rock" title="Rock" />
        </span>
        <span class="option" data-index="3">
            <img src="assets/scissor.png" alt="Scissor" title="Scissor" />
        </span>
        <span class="option" data-index="4">
            <img src="assets/spock.png" alt="Spock" title="Spock" />
        </span>
    </div>
</section>

Here if you see I have given custom attribute data-index to all the options because this way it is easy to access and update elements.

The .selected-option is empty at the beginning, whenever any option is selected it will be added here.

At the last there is a reset button, which will restart the round.

<!-- Reset button -->
<span class="reset"><span>Reset</span></span>

Complete HTML code

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Rock, Paper, Scissor, Lizard, Spock Game</title>
    <link rel="stylesheet" href="./index.css" />
  </head>
  <body>
    <main>
      <!-- Result -->
      <section id="result">
        <div id="score">
          <span id="player1-score" class="points">0</span>
          :
          <span id="player2-score" class="points">0</span>
        </div>
        <p id="round-message">Choose your option</p>
      </section>

      <!-- Player 1 area -->
      <section id="player1" class="players">
        <span class="name">Player1</span>
        <div class="selected-option">
          <span class="option" data-index="0"> </span>
        </div>
        <div class="available-options">
          <span class="option" data-index="0">
            <img src="assets/lizard.png" alt="Lizard" title="Lizard" />
          </span>
          <span class="option" data-index="1">
            <img src="assets/paper.png" alt="Paper" title="Paper" />
          </span>
          <span class="option" data-index="2">
            <img src="assets/rock.png" alt="Rock" title="Rock" />
          </span>
          <span class="option" data-index="3">
            <img src="assets/scissor.png" alt="Scissor" title="Scissor" />
          </span>
          <span class="option" data-index="4">
            <img src="assets/spock.png" alt="Spock" title="Spock" />
          </span>
        </div>
      </section>

      <!-- Player 2 area -->
      <section id="player2" class="players">
        <span class="name">Bot</span>
        <div class="selected-option">
          <span class="option" data-index="0"> </span>
        </div>
        <div class="available-options">
          <span class="option" data-index="0">
            <img src="assets/lizard.png" alt="Lizard" title="Lizard" />
          </span>
          <span class="option" data-index="1">
            <img src="assets/paper.png" alt="Paper" title="Paper" />
          </span>
          <span class="option" data-index="2">
            <img src="assets/rock.png" alt="Rock" title="Rock" />
          </span>
          <span class="option" data-index="3">
            <img src="assets/scissor.png" alt="Scissor" title="Scissor" />
          </span>
          <span class="option" data-index="4">
            <img src="assets/spock.png" alt="Spock" title="Spock" />
          </span>
        </div>
      </section>

      <!-- Reset button -->
      <span class="reset"><span>Reset</span></span>
    </main>
    <script src="./index.js"></script>
  </body>
</html>

Styling the game with CSS.

We have written the style code for each section separately and have used flex-box to aligns the elements.

* {
  box-sizing: border-box;
}

Score area.

main {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 80%;
  margin: 0 auto;
  flex-wrap: wrap;
}

/* Score style */
#result {
  flex: 1 100%;
}

#round-message,
#score {
  text-align: center;
}

#round-message {
  font-size: 1.5em;
  text-transform: capitalize;
}

#score {
  font-size: 3em;
  margin-bottom: 10px;
}

Player area

/* Player style */
.players {
  display: inline-flex;
  flex-wrap: wrap;
  flex: 1;
  padding: 10px 25px;
}

.selected-option,
.name,
.available-options {
  display: inline-flex;
  flex: 1 100%;
  padding: 15px 0;
}

.selected-option,
.name {
  justify-content: center;
}

.name {
  font-size: 1.8em;
  color: #f44336;
  font-weight: 600;
}

/* Options Style */
.available-options {
  justify-content: space-between;
}

.option {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  width: 75px;
  height: 75px;
  padding: 10px;
  border-radius: 50%;
  border: 1px solid #f44336;
  box-shadow: 0 0 3px;
  cursor: pointer;
}

.option.active {
  border-color: blue;
}

.option:hover > img {
  transform: scale(1.1);
}

.option > img {
  width: 70%;
  transition: all 0.2s ease;
}

.selected-option > .option {
  width: 110px;
  height: 110px;
}

/* Rotate the scissor of second player */
#player2 .option > img[alt="Scissor"] {
  transform: rotate(180deg);
}

#player2 .option:hover > img[alt="Scissor"] {
  transform: rotate(180deg) scale(1.1);
}

#player2 .option {
  cursor: not-allowed;
  background-color: #80cbc4;
}

I have rotated the scissor of the second player to give a feel that that it is pointing to the first player.

Reset button

/* Reset button */
.reset {
  display: inline-flex;
  flex: 1 100%;
  justify-content: center;
  margin-top: 20px;
}

.reset > span {
  font-size: 1.8em;
  cursor: pointer;
  border: 1px solid red;
  padding: 10px 15px;
  background-color: #f44336;
  box-shadow: 0 0 3px;
  color: #fff;
  transition: all 0.2s ease;
}

.reset > span:hover {
  color: #000;
}

Adding life to rock, paper, scissor, lizard, spock with javascript.

I like to define the constants at the top so that I have idea of how I should define my functions, these are the three constants I have figured out which I will be needing.

  1. Folder path for where images will be stored.
  2. Options image path and alt text.
  3. Rule for which option will win over other.
//Order in which options are available
const arr = [
  {
    image: "lizard.png",
    name: "Lizard",
  },
  {
    image: "paper.png",
    name: "Paper",
  },
  {
    image: "rock.png",
    name: "Rock",
  },
  {
    image: "scissor.png",
    name: "Scissor",
  },
  {
    image: "spock.png",
    name: "Spock",
  },
];

//Rule for who has win over whom
const rule = {
  Lizard: ["Spock", "Paper"],

  Paper: ["Rock", "Spock"],

  Rock: ["Lizard", "Scissor"],

  Scissor: ["Paper", "Lizard"],

  Spock: ["Scissor", "Rock"],
};

//Folder in which images are stored
const imageFolderPath = "assets";

Also I like to pull all the selectors and store them in a variable so that I can reuse them wherever possible, But you can do it however you feel like.

//All the options of player 1
const player1Options = document.querySelectorAll(
  "#player1 .available-options .option"
);

//All the options of bot
const botOptions = document.querySelectorAll(
  "#player2 .available-options .option"
);

//Where selected option of player 1 will be shown
const playerShowArea = document.querySelector(
  "#player1 .selected-option .option"
);

//Where selected option of bot will  be  shown
const botShowArea = document.querySelector("#player2 .selected-option .option");

//Player 1 and bot score
const player1Score = document.querySelector("#player1-score");
const player2Score = document.querySelector("#player2-score");

//Where message will be shown
const roundMessage = document.querySelector("#round-message");

Now when player selects an option we have to perform a series of process.

  1. Show the current selected option for player 1 and highlight the selected option.
  2. Pick a random option for bot and then highlight the picked option.
  3. Calculate the result to show who won the round and increase the points.

We will do this when user selects an option and for that we will have to listen to the click event.

player1Options.forEach((e) => {
  e.addEventListener("click", () => {
    play(e);
  });
});

This will invoke the play function whenever the option is selected. Inside the play function we will start all other process.

const play = (e) => {
  //Get the index of the option selected by player
  const player1 = e.getAttribute("data-index");

  //Number of options available
  const length = arr.length;

  //Generate a random number between number of options available for bot
  const player2 = Math.floor(Math.random() * length);

  //Show the player1 selected option and highlight it
  showPlayerOption(player1, playerShowArea);
  highlightSelectedOption(player1, player1Options);

  //Show the bot selected option
  showPlayerOption(player2, botShowArea);
  highlightSelectedOption(player2, botOptions);

  //Calculate the result
  calculateScore(player1, player2);
};

If you see I have created two different functions for showing the selected option and highlighting the selected option because I like to keep things separate for different use cases. But you can put them in a single function.

Arguments which these functions take are pretty straight forward.

To show the selected option it takes the index of the selected option and the element where it needs to be shown. Same to highlight the option, It take index and list of options element.

Now lets define these functions.

//Generate an image element
const generateImgElement = (index) => {
  const { image, name } = arr[index];
  const imgElement = document.createElement("img");
  imgElement.src = `${imageFolderPath}/${image}`;
  imgElement.alt = name;
  imgElement.title = name;
  return imgElement;
};

//Show selected option
const showPlayerOption = (index, showArea) => {
  //Append the generated image to the show area
  const imgElement = generateImgElement(index);
  showArea.innerHTML = "";
  showArea.append(imgElement);
};

const highlightSelectedOption = (index, options) => {
  //Remove the active class from all options
  options.forEach((e) => {
    e.classList.remove("active");
  });
  
  //Add the active class to the selected option
  options[index].classList.add("active");
};

Every time an option is selected an image will be added to the show area.

Last but not least, let see the function to calculate the result. When the result is calculated two actions take place.

  • Score is updated
  • Message is displayed to show who won the round
//Change the score
const addScore = (player) => {
    const { innerHTML } = player;
    player.innerHTML = Number(innerHTML) + 1;
};

//Show the message
const showMessage = (msg) => {
    roundMessage.innerHTML = "";
    roundMessage.innerHTML = msg;
};

const calculateScore = (player1, player2) => {
  //Player 1 choice
  const player1Choice = arr[player1].name;

  //Bot choice
  const player2Choice = arr[player2].name;

  //Get player 1 selected choice rule
  const player1Strength = rule[player1Choice];

  //Check the case and who wins the round
  if (player1Choice === player2Choice) {
    showMessage("draw");
  } else if (player1Strength.includes(player2Choice)) {
    //Update the score and show message who won the round
    addScore(player1Score);
    showMessage("player 1 wins");
  } else {
    //Update the score and show message who won the round
    addScore(player2Score);
    showMessage("Bot wins");
  }
};

At the end, create a function to reset everything when user clicks on the reset button.

const reset = () => {
  botShowArea.innerHTML = "";
  playerShowArea.innerHTML = "";
  roundMessage.innerHTML = "Choose your option";
  player2Score.innerHTML = "0";
  player1Score.innerHTML = "0";
  player1Options.forEach((e) => {
    e.classList.remove("active");
  });
  botOptions.forEach((e) => {
    e.classList.remove("active");
  });
};

document.querySelector(".reset").addEventListener("click", reset);