Learn concepts of Vanilla JavaScript while building a Privacy Card Game in 5 easy Steps.
Tags: javascript, gamedev, vanillajs, basics
Published: 2020-08-04 03:27:52

## Is Vanilla JS even important to learn?

VanillaJS is a name to refer to using plain JavaScript without any additional libraries.

Now you may ask — Is it worth to spend time on learning Vanilla JS when we have so many frameworks around making development process and efficient for us?

> If your knowledge is limited only to a single JavaScript framework, you will have a hard time learning a new one. All the JavaScript frameworks and libraries are inevitably based on its Vanilla core. This should convince anyone that knowledge of a pure JavaScript is a must in the long run. — David Kopal

That said, it is true that investing time in Vanilla JavaScript will pay off in the long run. So keeping this significance of Vanilla JS in mind, let’s dive in this tutorial to understand JavaScript concepts along with creating an interesting game.

---

## What are we making?

![Hacket - the Privacy Card Game](https://raw.githubusercontent.com/Megha-Pathak/Hacket/master/github-resources/HacketDemo2.gif)

[Demo](https://hacket.netlify.com/) 

## Story behind this Project

If you are into stories like me, then you might be interested in knowing — How I came up with the idea of this project? (If you are not interested in knowing this story, skip to the next section to understand the Project Structure).

Some years ago, when I was in school, I attended a seminar about Cyber Security. So when I came across an article about Cybersecurity few months aback, it strikes to me that the whole seminar I attended back in school can be converted to a game to educate kids about Internet Privacy. So Let's Begin. 

---

## Step 1  - Understanding Project Structure and Setting Up files

I personally use Visual Studio Code, you can use any IDE you like. The first step is to create the following files with the right extension attached to them.

![project structure](https://cdn.hashnode.com/res/hashnode/image/upload/v1596450263059/uufoGpYfy.png)

---

## Step 2  - Creating skeleton of the project with HTML

Now we will create the basic skeleton of our project using HTML. This file will be called **index.html**.

Give your brain time to think, what will constitute to the structure of this game. 

![Screenshot_2020-08-02 Hacket.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1596384474362/l94m2kvtF.png)

Now start writing down the components - 

- You are creating a game, so ofcourse there will be a **game-board**. 
- Then the game board is divided into **hacker-area** and **player-area**. 
- We are also needed to recorded Life value of both Hacker and You, so that we can calculate the winner. Let's say these **stats**, **hacker-stats**, **player-stats**, **life-bar**, **life-left**, **life-total**. 
- We also have cards and text(scenarios) written on them, with certain power as well that is revealed after the player chooses any card. We will call these as, **card**, **hacker-card**, **player-card**, **text** and **power**. 
-  We have few buttons such as, **Next**(to bring the next scenarios to the player), **start-game** button to start the game. 
- Lastly, we have a **winner-section** to show the **winner-message** to notify the player, whether s(he) lost or won against the hacker, and the option for playing the game again, restart. 

Each part we discussed in the above points, will serve as an individual class. We have used classes for dividing the components, so that we can use `document.querySelectorAll` for adding behavior to our game. 

You can read more about `queryselector` and `classes` here -  [document.querySelectorAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) and [Classes in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)
 
Now we will create the <div> tag to define the above divisions. Like this - 
```
<div class="game-board">
    <div class="hacker-area">
        <h1>
          <strong>HACKER CARDS</strong> <br/> Choose the best card to stop the hackers' attack
        </h1>
    </div>
</div>
```

Note - Nest div sections in the right way. Ex - All the other divisions will come under game board, life-bar will come under stats and so on. 

Try yourself to create the whole HTML skeleton like this, you can also refer to the code file [here](https://raw.githubusercontent.com/Megha-Pathak/Hacket/master/index.html).

---

## Steps 3 -  Adding Scenarios using a JavaScript Object

Now we will be adding scenarios with a JavaScript Object. There will be 1 hackerCard per scenario and 3 playerCards per scenario. We will write these in the **cards.js** file. Scenario number 1 will look like this - 

```
var scenarios = [
  {
    hackerCard : {
      description : "I set up a fake Wi-Fi station to steal people’s email and track them online.",
      power : 4,
    },
playerCards : [
      {
        description : "I never use public wifi networks.",
        power : 5,
      },
      {
        description : "I browse the web, but I never do any personal business on a public wifi network.",
        power : 3,
      },
      {
        description : "I connect to any wifi network I can use in public.",
        power : 1,
      }
    ]
  },
];
```
You can add as many as scenarios you want. I have kept 8 different scenarios, you can refer to them [here](https://raw.githubusercontent.com/Megha-Pathak/Hacket/master/cards.js) 

Read more about Objects in JavaScript [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)

---

## Step 4  -  Adding behavior to the project with a Script

Our **script.js** file will contain several functions that will add behavior to our projects. So let's start writing these functions. 

### First things first, set the starting life totals here

```
var playerLife = 5;
var hackerLife = 5;
```
Read more about variable declaration [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var)

### Now the Message that will appear when the game is over
```
var hackerWinnerMessage = "Game over: You got hacked!";
var playerWinnerMessage = "You defeated the hacker!";
```

### Game code starts here
```
var playerStartLife = parseInt(playerLife);
var hackerStartLife = parseInt(hackerLife);

var roundFinished = false;
var cardSelected = false;

updateScores();

document.querySelector(".game-board").classList.add("before-game");

var allCardElements = document.querySelectorAll(".card");
```

### Now let's add click handler to all player card elements
for(var i = 0; i < allCardElements.length; i++) {
  var card = allCardElements[i];
  if(card.classList.contains("player-card")) {
    card.addEventListener("click",function(e){
      cardClicked(this);
    });
  }
}


### We need a function that will execute when a card is clicked
```
function cardClicked(cardEl) {

  if(cardSelected) { return; }
  cardSelected = true;

  cardEl.classList.add("played-card");

  document.querySelector(".game-board").classList.add("card-selected");
```

  ### To wait 500ms to reveal the hacker power
```
  setTimeout(function(){
    revealHackerPower();
  },500)
```
### Waits for 750ms to reveal the player power
 
```
 setTimeout(function(){
    revealPlayerPower();
  },800)
``` 

### Waits for 1250ms to compare the card scores
 ```
 setTimeout(function(){
    compareCards();
  }, 1400);
}
```

Read more about the set timed out function [here](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout). 

### Now we gotta show the power level on the player card
```
function revealPlayerPower(){
  var playerCard = document.querySelector(".played-card");
  playerCard.classList.add("reveal-power");
}
```
Read more about classList and its functionality [here](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList).
### Similarly a function to show the power level on the hacker card
```
function revealHackerPower(){
  var hackerCard = document.querySelector(".hacker-card");
  hackerCard.classList.add("reveal-power");
}
```
### A function for comparing the cards based on their power value. 
```
function compareCards(){
  var playerCard = document.querySelector(".played-card");
  var playerPowerEl = playerCard.querySelector(".power");

  var hackerCard = document.querySelector(".hacker-card");
  var hackerPowerEl = hackerCard.querySelector(".power");

  var playerPower = parseInt(playerPowerEl.innerHTML);
  var hackerPower = parseInt(hackerPowerEl.innerHTML);

  var powerDifference = playerPower - hackerPower;

  if (powerDifference < 0) {
    // Player Loses
    playerLife = playerLife + powerDifference;
    hackerCard.classList.add("better-card");
    playerCard.classList.add("worse-card");
    document.querySelector(".player-stats .thumbnail").classList.add("ouch");
  } else if (powerDifference > 0) {
    // Player Wins
    hackerLife = hackerLife - powerDifference;
    playerCard.classList.add("better-card");
    hackerCard.classList.add("worse-card");
    document.querySelector(".hacker-stats .thumbnail").classList.add("ouch");
  } else {
    playerCard.classList.add("tie-card");
    hackerCard.classList.add("tie-card");
  }

  updateScores();

  if(playerLife <= 0) {
    gameOver("Hacker");
  } else if (hackerLife <= 0){
    gameOver("Player")
  }

  roundFinished = true;

  document.querySelector("button.next-turn").removeAttribute("disabled");
}
```
### Once the game is over we will use the following function to show the winner message. 
```
function gameOver(winner) {
  document.querySelector(".game-board").classList.add("game-over");
  document.querySelector(".winner-section").style.display = "flex";
  document.querySelector(".winner-section").classList.remove("player-color");
  document.querySelector(".winner-section").classList.remove("hacker-color");

  if(winner == "Hacker") {
    document.querySelector(".winner-message").innerHTML = hackerWinnerMessage;
    document.querySelector(".winner-section").classList.add("hacker-color");
  } else {
    document.querySelector(".winner-message").innerHTML = playerWinnerMessage;
    document.querySelector(".winner-section").classList.add("player-color");
  }
}
```

### When the Start button is clicked, this function will Start the game
```
function startGame() {
  document.querySelector(".game-board").classList.remove("before-game");
  document.querySelector(".game-board").classList.add("during-game");
  playTurn();
}
```

### This function updates the displayed life bar and life totals
```
function updateScores(){

  // Update life totals for each player
  document.querySelector(".player-stats .life-total").innerHTML = playerLife;
  document.querySelector(".hacker-stats .life-total").innerHTML = hackerLife;

  // Update the player lifebar
  var playerPercent = playerLife / playerStartLife * 100;
  if (playerPercent < 0) {
    playerPercent = 0;
  }
  document.querySelector(".player-stats .life-left").style.height =  playerPercent + "%";

  // Update the hacker lifebar
  var hackerPercent = hackerLife / hackerStartLife * 100
  if (hackerPercent < 0) {
    hackerPercent = 0;
  }
  document.querySelector(".hacker-stats .life-left").style.height =  hackerPercent + "%";
}
```

### For shuffling the set of 3 player cards at each run we will use this shuffleArray function 
```
function shuffleArray(a) {
  var j, x, i;
  for (i = a.length; i; i--) {
    j = Math.floor(Math.random() * i);
    x = a[i - 1];
    a[i - 1] = a[j];
    a[j] = x;
  }
  return a;
}
```
Read more about Math.random [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random).
### Plays one turn of the game
```
function playTurn() {

  roundFinished = true;
  cardSelected = false;

  document.querySelector(".game-board").classList.remove("card-selected");

  // Remove "ouch" class from player and hacker thumbnails
  document.querySelector(".hacker-stats .thumbnail").classList.remove("ouch");
  document.querySelector(".player-stats .thumbnail").classList.remove("ouch");

  // Hides the "next turn" button, will show again when turn is over
  document.querySelector(".next-turn").setAttribute("disabled", "true");

  for(var i = 0; i < allCardElements.length; i++) {
    var card = allCardElements[i];
    card.classList.remove("showCard");
  }

  setTimeout(function(){
    revealCards();
  }, 500);
}
```
### For revealing cards to the player
```
function revealCards(){
  var j = 0;
  var cardIndexes = shuffleArray([0, 1, 2]);

  // Get scenario cards
  console.log("scenarios.length == " + scenarios.length);

  var randomScenarioIndex = Math.floor(Math.random() * scenarios.length);
  var scenario = scenarios[randomScenarioIndex];
  console.log(scenario.hackerCard.description);

  scenarios.splice(randomScenarioIndex, 1);

  console.log("scenarios.length after splice == " + scenarios.length);

  var hackerCard = scenario.hackerCard;
  var hackerCardEl = document.querySelector(".hacker-area .card");

  // Contents of the player cards
  var playerCards = scenario.playerCards;

  for(var i = 0; i < allCardElements.length; i++) {
    var card = allCardElements[i];

    card.classList.remove("worse-card");
    card.classList.remove("better-card");
    card.classList.remove("played-card");
    card.classList.remove("tie-card");
    card.classList.remove("prepared");
    card.classList.remove("reveal-power");

    // Display the payer card details
    if(card.classList.contains("player-card")) {
      card.querySelector(".text").innerHTML = playerCards[cardIndexes[j]].description;
      card.querySelector(".power").innerHTML = playerCards[cardIndexes[j]].power;
      j++;
    }

    // Reveal each card one by one with a delay of 100ms
    setTimeout(function(card, j){
      return function() {
        card.classList.remove("prepared");
        card.style.display = "block";
        card.classList.add("showCard");
      }
    }(card,i), parseInt(i+1) * 200);
  }

  // Display the hacker card
  hackerCardEl.querySelector(".text").innerHTML = hackerCard.description;
  hackerCardEl.querySelector(".power").innerHTML = hackerCard.power;
}
```
Read more about innerHTML [here](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML). 
You can access to my **scripts.js ** file [here](https://raw.githubusercontent.com/Megha-Pathak/Hacket/master/scripts/script.js)
---

## Step 5 - Styling the Project

Congratulations! You have covered a big chunk of work till now, give yourself a pat on the back. 
Now we will be adding styling to our project. 

### Styling that can be customized 
First we have the customize.css file, that have the basic colors and background images that can be changed anytime to change the whole appearance of the project. 
```
/* Change the colors in this stylesheet to customize the game. */ 

/* Player Styles */

.player-color {
  background-color: #2a79d0;
}

.player-area {
  background-color: #0d3158;
  background-image: url(images/chip.svg);
}

.player-card .power {
  background-image: url(images/shield.svg);
}

/* Hacker Styles */

.hacker-color {
  background-color: #ce3d20;
}

.hacker-area {
  background-color: #3c1912;
  background-image: url(images/chip.svg);
}

.hacker-card .power {
  background-image: url(images/skull.svg);
}
```
Note - The changes made to the customize.css file doesn't affect the functionality. 
You can access the images required for the styling from [here](https://github.com/Megha-Pathak/Hacket/tree/master/images)

### Styling that adds to the functionality 
You can try styling all the elements by using class selectors and the reveal animations by yourself, or you can simply take help of this [file](https://raw.githubusercontent.com/Megha-Pathak/Hacket/master/styles/style.css)

## What's Next?
If you have followed this article till now, you surely would be able to pull of the creation of whole game successfully. If you encounter any problem you can always refer to my code [here](https://github.com/Megha-Pathak/Hacket/). 

If you liked this tutorial, share it with your friends and help them in creating there first JavaScript Project. 

Stay tuned until the next simplified bomb of knowledge lands to your feed. 

Keep Exploring! Keep Coding!