Simple Memory Game in Vanilla Javascript – Free Code Download

Welcome to a beginner’s tutorial on how to create a simple memory game in Javascript. Interested to learn how to create browser games in Javascript? Or looking for a simple project to do? Then the class memory game is a good place to get started since it is not too complicated – Read on to find out!

ⓘ I have included a zip file with all the example source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

TABLE OF CONTENTS

Download & Demo How to Play The Scripts
Useful Bits & Links The End

 

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

EXAMPLE CODE DOWNLOAD

Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

QUICK NOTES

There is nothing to install, so just download and unzip into a folder. If you spot a bug, please feel free to comment below. I try to answer questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

MEMORY GAME DEMO

 

 

HOW TO PLAY

Before we touch on the code, let us start with the basic “how to play”… Let us not judge the people who don’t know. Feel free to skip this section if you already know the basic game rules.

 

GAME RULES

The rules for the memory game is very straightforward:

  • There are a number of “cards” that are scattered randomly across a grid in pairs.
  • The player opens a pair of cards in the grid – If the cards match, that is “one point” and the player will continue to find the next pair.
  • But if the cards don’t match, they will “fold back” to become hidden.
  • The player will have to try to remember all the positions of the cards and match all of them to win.

That’s all.

 

 

THE SCRIPTS

Now that we are done with the “how to play”, let us move forward to build the game itself.

 

THE HTML

memory.html
 <!-- (A) CSS + JS -->
<link rel="stylesheet" href="memory.css">
<script src="memory.js"></script>
 
<!-- (B) GAME CONTAINER -->
<div id="mem-game"></div>

The HTML is probably the easiest component… Just a simple <div id="mem-game"> to render the game. That’s all.

 

THE CSS

memory.css
/* (A) CONTAINER */
#mem-game {
  display: grid;
  grid-template-columns: auto auto auto auto;
  grid-gap: 10px;
  max-width: 420px;
  margin: 0 auto;
}

/* (B) CARDS */
.mem-card {
  padding: 10px;
  cursor: pointer;
  background: #e5e5e5;
}
.mem-card.open { background: #fea; }
.mem-card.right { background: #b3ffc0; }
.mem-card.wrong { background: #ffb6b6; }

Yep, very simple CSS as well.

  • All we need here is essentially display: grid, grid-template-columns: auto auto auto auto, and CSS will literally do the rest of the layout magic.
  • The rest are kind of self-explanatory too.
    • .mem-card.open is the style for opened cards.
    • .mem-card.right for cards that are correctly matched.
    • .mem-card.wrong for cards that are wrongly matched.

 

 

THE JAVASCRIPT

memory.js
var mem = {
  // (A) PROPERTIES
  // (A1) HTML ELEMENTS
  hWrap : null, // HTML game wrapper
  hCards : null, // HTML cards
  // (A2) GAME SETTINGS
  sets : 6, // Total number of cards to match
  hint : 1000, // How long to show mismatched cards
  url : "", // Optional, URL to images
  // (A3) FLAGS
  loaded : 0, // Total number of assets loaded
  moves : 0, // Total number of moves
  last : null, // Last opened card
  grid : null, // Current play grid
  matches : null, // Current matched cards
  locked : null, // 2 cards chosen did not match

  // (B) PRELOAD
  preload : function () {
    // (B1) GET HTML GAME WRAPPER
    mem.hWrap = document.getElementById("mem-game");

    // (B2) PRELOAD IMAGES
    for (let i=0; i<=mem.sets; i++) {
      let img = document.createElement("img");
      img.onload = function(){
        mem.loaded++;
        if (mem.loaded == mem.sets+1) { mem.init(); }
      };
      img.src = mem.url+"smiley-"+i+".png";
    }
  },
  
  // (C) INIT GAME
  init : function () {
    // (C1) RESET
    if (mem.locked != null) { 
      clearTimeout(mem.locked);
      mem.locked = null;
    }
    mem.hCards = [];
    mem.grid = [];
    mem.matches = [],
    mem.moves = 0;
    mem.last = null;
    mem.locked = null;
    mem.hWrap.innerHTML = "";

    // (C2) RANDOM RESHUFFLE CARDS
    // Credits : https://gomakethings.com/how-to-shuffle-an-array-with-vanilla-js/
    let current = mem.sets * 2, temp, random;
    for (var i=1; i<=mem.sets; i++) {
      mem.grid.push(i);
      mem.grid.push(i);
    }
    while (0 !== current) {
      random = Math.floor(Math.random() * current);
      current -= 1;
      temp = mem.grid[current];
      mem.grid[current] = mem.grid[random];
      mem.grid[random] = temp;
    }
    
    // (C3) CREATE HTML CARDS
    for (let i=0; i`;
      let card = document.createElement("div");
      card.className = "mem-card";
      card.innerHTML = `<img src='${mem.url}smiley-0.png'/>`;
      card.dataset.idx = i;
      card.onclick = mem.open;
      mem.hWrap.appendChild(card);
      mem.hCards.push(card);
    }
  },
  
  // (D) OPEN CARD
  open : function () { if (mem.locked == null) { 
    // (D1) OPEN SELECTED CARD
    mem.moves++;
    let idx = this.dataset.idx;
    this.innerHTML = `<img src='${mem.url}smiley-${mem.grid[idx]}.png'/>`;
    this.onclick = "";
    this.classList.add("open");
    
    // (D2) NO PREVIOUS GUESS - JUST RECORD AS OPENED
    if (mem.last == null) { mem.last = idx; }
    
    else {
      // (D3) MATCHED AGAINST PREVIOUS GUESS
      if (mem.grid[idx] == mem.grid[mem.last]) {
        mem.matches.push(mem.last);
        mem.matches.push(idx);
        mem.hCards[mem.last].classList.remove("open");
        mem.hCards[idx].classList.remove("open");
        mem.hCards[mem.last].classList.add("right");
        mem.hCards[idx].classList.add("right");
        mem.last = null;
        if (mem.matches.length == mem.sets * 2) {
          alert("YOU WIN! TOTAL MOVES " + mem.moves);
          mem.init();
        }
      }

      // (D4) NOT MATCHED - CLOSE BOTH CARDS ONLY AFTER A WHILE
      else {
        mem.hCards[mem.last].classList.add("wrong");
        mem.hCards[idx].classList.add("wrong");
        mem.locked = setTimeout(function(){
          mem.close(idx, mem.last);
        }, mem.hint);
      }
    }
  }},

  // (E) CLOSE PREVIOUSLY MIS-MATCHED CARDS
  close : function (aa, bb) {
    aa = mem.hCards[aa];
    bb = mem.hCards[bb];
    aa.classList.remove("wrong");
    bb.classList.remove("wrong");
    aa.classList.remove("open");
    bb.classList.remove("open");
    aa.innerHTML = `<img src='${mem.url}smiley-0.png'/>`;
    bb.innerHTML = `<img src='${mem.url}smiley-0.png'/>`;
    aa.onclick = mem.open;
    bb.onclick = mem.open;
    mem.locked = null;
    mem.last = null;
  }
};

// (F) INIT GAME
window.addEventListener("DOMContentLoaded", mem.preload);

 

 

JAVASCRIPT EXPLANATION

Holy cow! The Javascript looks like potential brain damage material. But look carefully, and it is actually not that massive.

  • The var mem object literally contains all the backbone mechanics of the game.
  • There are only 4 functions inside – preload(), init(), open(), and close().

Not to be intimidated, let us do a quick run-through of the Javascript section-by-section.

 

PRELOADING

The preload() function is where the game starts, and all it does is essentially preload all the required images. Quick notes:

  • mem.sets are the total pairs of cards to match. That is, 6 sets will mean the players have to match a total of 12 cards.
  • This function preloads the card images, from smiley-0.png to smiley-6.png.
  • smiley-0.png is the “default” closed card image.
  • If you want to add more sets, simply change mem.sets add more simley-x.png images.
  • Lastly, define mem.url if you want to use absolute URL for the images. For example, mem.url = "http://mysite.com/assets/".

 

GAME INITIALIZE

The init() function fires up after the preloading is done. Not going to explain line-by-line here, but it basically resets all the game flags, reshuffles the cards randomly, and redraws the HTML. There are quite a lot of flags used in this game, but 2 important ones to know at this stage are:

  • The shuffled cards will be put inside the mem.grid array.
  • The corresponding HTML cards will be put inside mem.hCards.

Also, if you want to allow a game reset at any time, just call the init() function.

 

 

GAMEPLAY

Lastly, open() is fired when the player opens a card and close() will hide mismatched pairs. Quite confusing, but we are essentially working with various flags to keep track:

  • Firstly, we will store the selected card into mem.last.
  • mem.moves is a counter that tracks the number of moves.
  • On selecting a second card (mem.last  is not null), we check against mem.grid to see if it is a match.
  • Matched cards will be kept inside mem.matches. To check if the player won, we simply check if all cards have been matched. I.E. mem.matches.length == mem.sets * 2.
  • On a mismatch, we do not immediately close the cards. We set a timer on mem.locked to keep them open for a while before calling close() to hide them; While mem.locked is engaged, we cannot open other cards.

 

USEFUL BITS & LINKS

That’s all for this project, and here is a small section on some extras and links that may be useful to you.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

1 thought on “Simple Memory Game in Vanilla Javascript – Free Code Download”

Leave a Comment

Your email address will not be published. Required fields are marked *