If you're seeing this message, it means we're having trouble loading external resources on our website.

If you're behind a web filter, please make sure that the domains *.kastatic.org and *.kasandbox.org are unblocked.

Main content

Memory game: Drawing a grid of tiles

The first step of playing the "Memory" game is to randomly shuffle all the tiles, and then lay them out in a rectangular grid, face down so that we can't see which image is on the other side of each tile.

Face-down tiles

To start off in programming the game, let's just worry about creating face-down tiles, and figure out how to do the different images later.
The "tile" is an important enough object in the game of "Memory" that we will use object-oriented principles to define a Tile object and then create multiple instances of it. Then we'll be able to associate both properties (like location and image) as well as behavior (like drawing them) with each of the Tiles.
To start off, we'll define the Tile constructor function. Since we're not dealing with the images yet, we'll just pass x and y arguments to it. We'll also remember the tile size (a constant) in a property on the object.
var Tile = function(x, y) {
  this.x = x;
  this.y = y;
  this.size = 50;
};
Now that we've defined the constructor, we can use that in a loop to create tiles at appropriate x and y positions. In fact, we'll use two for loops - a nested for loop - as that makes it conceptually easy to generate coordinates for a grid.
First we need to declare an empty tiles array, to store all those tiles:
var tiles = [];
Our outer loop iterates for as many columns as we want, our inner loop iterates for each of the rows, and each new Tile is initialized with an x and y that corresponds to that row and column.
var NUM_COLS = 5;
var NUM_ROWS = 4;
for (var i = 0; i < NUM_COLS; i++) {
  for (var j = 0; j < NUM_ROWS; j++) {
    var tileX = i * 54 + 5;
    var tileY = j * 54 + 40;
    tiles.push(new Tile(tileX, tileY));
  }
}
But, uh, it's hard to know if the tiles will look good, because we don't have any code to draw them yet! In fact, maybe we should have done that first. Sometimes, in programming, it's hard to know what to do first, aye? Let's now add a method to the Tile object that draws a tile face-down on the canvas. We'll draw a rounded rectangle with a cute Khan leaf on top, at the assigned location.
Tile.prototype.draw = function() {
  fill(214, 247, 202);
  strokeWeight(2);
  rect(this.x, this.y, this.size, this.size, 10);
  image(getImage("avatars/leaf-green"),
        this.x, this.y, this.size, this.size);
};
We're so close to being able to check how our tiles look! Let's add a new for loop that iterates through all the tiles and calls the drawing method on them:
for (var i = 0; i < tiles.length; i++) {
    tiles[i].draw();
}
Here's what our program looks like, with all that code. Try tweaking the different numbers in the nested for loop to see how it changes the grid or changing how they're drawn (different logo, perhaps?)

Face-up tiles

Now that we've got a grid of face-down tiles, let's tackle a bit of a trickier problem: assigning each of them an image, such that there's 2 of every image in the array, randomly distributed throughout. There are likely many ways we could accomplish this, but here's what I'd suggest:
  1. We create an array of the possible images, using the getImage function to pick ones from our library.
  2. We'll only need 10 images for the faces of our 20 tiles, so then we create a new array that holds 2 copies of 10 randomly selected images from that first array.
  3. We shuffle the selected images array, so that the pairs of images are no longer next to each other in an array.
  4. In the nested for loop where we create the tiles, we'll assign an image from that array to each tile.
Those steps may not make sense yet - let's do them each and see what they look like.
Step 1: We create an array of the possible images, using the getImage function to pick ones from our library:
var faces = [
    getImage("avatars/leafers-seed"),
    getImage("avatars/leafers-seedling"),
    getImage("avatars/leafers-sapling"),
    getImage("avatars/leafers-tree"),
    getImage("avatars/leafers-ultimate"),
    getImage("avatars/marcimus"),
    getImage("avatars/mr-pants"),
    getImage("avatars/mr-pink"),
    getImage("avatars/old-spice-man"),
    getImage("avatars/robot_female_1"),
    getImage("avatars/piceratops-tree"),
    getImage("avatars/orange-juice-squid")
];
I picked a bunch of avatars, but you could change it to pick whatever your favorite images are. The important thing is to make sure that this array has at least 10 images in it, so that we don't run out of images for our 20 tiles. We can add lots more than 10 images though, to give our game more variety each time its played, because we'll narrow down the list in the next step.
Step 2: We'll only need 10 images for the faces of our 20 tiles, so then we create a new array that holds 2 copies of 10 randomly selected images from that first array.
To do that, we create a for loop that iterates 10 times. In each iteration, we randomly pick an index from the faces array, push that twice onto the selected array, and then use the splice method to remove it from the faces array, so that we don't select it twice. That last step is very important!
var selected = [];
for (var i = 0; i < 10; i++) {
    // Randomly pick one from the array of faces
    var randomInd = floor(random(faces.length));
    var face = faces[randomInd];
    // Push 2 copies onto array
    selected.push(face);
    selected.push(face);
    // Remove from faces array so we don't re-pick
    faces.splice(randomInd, 1);
}
Step 3: We shuffle the selected images array, so that the pairs of images are no longer next to each other in an array.
You've probably shuffled a deck of cards in your life, but have you ever shuffled an array in JavaScript? The most popular technique for shuffling in any programming language is called the Fisher-Yates Shuffle, and that's what we'll use here.
The Fisher-Yates Shuffle starts by first selecting a random element anywhere in the array, and swapping it with the last element in the array. In the next step, it selects a random element from anywhere in the array besides the last element, and swaps it with the second to last element. It keeps going until it has swapped every element.
You can click through this visualization to see what I mean:
To implement that in JavaScript, let's make a shuffleArray function that takes in an array and shuffles its elements, changing the original array:
var shuffleArray = function(array) {
    var counter = array.length;

    // While there are elements in the array
    while (counter > 0) {
        // Pick a random index
        var ind = Math.floor(Math.random() * counter);
        // Decrease counter by 1
        counter--;
        // And swap the last element with it
        var temp = array[counter];
        array[counter] = array[ind];
        array[ind] = temp;
    }
};
If the algorithm doesn't quite make sense yet after stepping through the visualization and reading the code, you could try it with an actual deck of cards in the real world or watch how Adam Khoury does it in his Youtube video.
After defining that function, we need to actually call it:
shuffleArray(selected);
And now we have an array of 10 pairs of images, randomly shuffled!
Step 4: In the nested for loop where we create the tiles, we'll assign an image from that array to each tile.
We have 20 images in our selected array, and we're iterating 20 times to instantiate new tiles at locations in the grid. To select a random image for each tile, we can call the pop method on the array. That method removes the last element from the array and returns it, and is the easiest way to make sure we assign all the images but don't double assign them.
for (var i = 0; i < NUM_COLS; i++) {
  for (var j = 0; j < NUM_ROWS; j++) {
    var tileX = i * 54 + 5;
    var tileY = j * 54 + 40;
    var tileFace = selected.pop();
    var tile = new Tile(tileX, tileY, tileFace);
    tiles.push(tile);
  }
}
Notice how that code passes tileFace as the third parameter to the Tileconstructor? Our constructor originally only had 2 parameters, x and y, but now we modify it so that we can also remember the image of each tile face, plus whether it's face up:
var Tile = function(x, y, face) {
    this.x = x;
    this.y = y;
    this.size = 70;
    this.face = face;
    this.isFaceUp = false;
};
So we now theoretically have images assigned to each tile, but we're not displaying them yet! Let's modify the Tile.draw method so that it can draw tiles that are face up:
Tile.prototype.draw = function() {
    fill(214, 247, 202);
    strokeWeight(2);
    rect(this.x, this.y, this.size, this.size, 10);
    if (this.isFaceUp) {
        image(this.face, this.x, this.y,
              this.size, this.size);
    } else {
        image(getImage("avatars/leaf-green"),
              this.x, this.y, this.size, this.size);
    }
};
Finally, to test it all works, we can change our for loop to set each tile's isFaceUp property to true before drawing it:
for (var i = 0; i < tiles.length; i++) {
  tiles[i].isFaceUp = true;
  tiles[i].draw();
}
Here it is, all together. Try restarting it to see how the tiles change each time.

Want to join the conversation?

  • duskpin sapling style avatar for user gdtwing
    how are the rectangles rounded out?
    (59 votes)
    Default Khan Academy avatar avatar for user
  • spunky sam blue style avatar for user ElOtroMiqui
    Some days ago I was wondering if there was something like .push, but opposite (instead of adding something to the array, deleting something from the array), and now I see it's .splice.

    How are those things called? the ".push" and the ".splice" thing? I mean, what do I have to look for on google to find more like them?

    Also, in this part:
    // Remove from faces array so we don't re-pick
    faces.splice(randomInd, 1);

    Why did you write a "1" right before the randomInd var? What does that 1 do?
    (30 votes)
    Default Khan Academy avatar avatar for user
    • leafers sapling style avatar for user Peter Collingridge
      The 1 in splice tells it to remove one item, so you can change this to remove multiple items. In addition to splice, which allows you to remove a variable number of items from any position in the array (and also add items at the same time), there are also:
      array.pop() - removes the last item in the array (opposite of push).
      array.shift() - removes the first item in the array (opposite of unshift).

      All of these methods return the value of the item they remove. e.g
      var a = [1, 2, 3];
      var x = a.pop();

      This will result in a equalling [1, 2] and x equalling 3.
      (56 votes)
  • duskpin ultimate style avatar for user Abhilash Mukerjee
    Why is floor( ); being used instead of round( ); ?
    (8 votes)
    Default Khan Academy avatar avatar for user
  • leaf orange style avatar for user Bryce
    I don't understand how you use the splice function. If someone could explain this more it would be great! Thank you!
    (6 votes)
    Default Khan Academy avatar avatar for user
  • duskpin ultimate style avatar for user luoyuqianluoyuqian
    Can anyone explain how
    selected.sort(function() {
    return 0.5 - random();
    });
    can randomize the array?
    (9 votes)
    Default Khan Academy avatar avatar for user
    • old spice man green style avatar for user Bob Lyon
      What a horrible way to introduce sorting! Sorting is used to bring order to data. This example uses sorting to accomplish the opposite goal.

      All sorting depends on the ability to compare two pieces of data, a, and b and tell if a>b, a===b, or a<b. If you read about the Javascript array sort method, it specifies how to communicate the results of each comparison. For example, if I were sorting an array of numbers nums, I might write a number compare function that looks like
      /*
      * Compare a to b, and return a positive number
      * if a>b, a negative number if a<b, and zero
      * when they are equal.
      */
      var compareNums = function(a, b) {
      if (a > b) {
      return 42;
      } else if (a < b) {
      return -3.14;
      } else {
      return 0;
      }
      };
      and then sort the array of numbers by invoking nums.sort(compareNums);

      So, this program "sorts" an array of faces and provides a comparison function that ignores its arguments (it doesn't even declare parameters) and randomly returns numbers between -0.5 and +0.5 (because random invoked with no arguments returns a number between zero and one, which has 0.5 subtracted from it.)

      The results of using this unreliable, random comparator is an array of elements that is anything but sorted.
      (2 votes)
  • hopper cool style avatar for user Joy Liu
    What is the function this? Also, could it get any jpg image from the web?
    (4 votes)
    Default Khan Academy avatar avatar for user
  • blobby green style avatar for user Selassie
    what is the difference between math.floor and floor
    (6 votes)
    Default Khan Academy avatar avatar for user
  • aqualine ultimate style avatar for user GrandMasterofTheOrder
    I get the Oh Noes guy and he is saying this
    Cannot read property 'width' of undefined

    can someone please help?
    (7 votes)
    Default Khan Academy avatar avatar for user
  • primosaur ultimate style avatar for user atoMickipedia⚛
    Which sort method does Chrome use?
    How about Edge?
    According to BobLyon, browsers don't use bubble sort.
    https://www.khanacademy.org/profile/BobLyon/discussion
    I know randomizing via sort is computationally inefficient; typing-wise though, does it use the least number of characters to get the job done?
    (5 votes)
    Default Khan Academy avatar avatar for user
  • blobby green style avatar for user natashaonline1111
    for those like me stuck at the randomInd variable in step 2 it took me a hwile to figure this out; for those like me, hope this helps:
    var randomInd = floor(random(faces.length));
    we need a random image from our array of faces..so we use a code to get a random index(randomInd); first to assign random value we assign a minimum and maximum to it..here it is the number of elements in our array (faces.length) and then since random can return a number in decimals we need to round it down (not up as we want to access the lower number so all indices are covered in case rounding up is greater than the number of elements in that array)
    so assign faces.length as minimum and maximum values to random and use floor to round it down
    this automatically gives us that if we want any one image from our selected ones we make the variable as follows:
    var face(any new image) = faces[randomInd];(randomly chosen from our selected images array by using the random index code)
    if am incorrect please those who do understand correct me..TIA
    (4 votes)
    Default Khan Academy avatar avatar for user
    • aqualine ultimate style avatar for user AD Baker
      It sounds like you understand what is happening in the code.

      Just one note. The syntax (or rules) of the random() function is
              random(min, max);
      If only one argument is passed to random(), that argument becomes the max and the function assigns 0 as the min argument.
              random(faces.length)
      will return a random value between 0.000 and the value of the length of the faces array. You are correct about why we round down.
      (5 votes)