Memo in 5 minutes

About

Memo is an exaple of memorisation and matching games. There can be many variations but one of the most popular is based of finding a pair of cards.
Game starts with a grid of shuffled card pairs. A player sees the grid opened (i.e. all cards visible) for a short period of time and player's goal is to memorise as much as possible.
After it the game starts - all cards become hidden as well.
On each move player opens a pair of cards. If they are the same - the cards are left open. Otherwise - the cards get closed.
Game ends when all cards are open.
This project will implement exactly this version of the game. For it we will use vanilla JavaScript without any libraries.

Link to Wikipedia: https://en.wikipedia.org/wiki/Matching_game

Live coding

Here is a live coding session that shows how it can be done.

Duration: 4:23 (coding - 4:14)

Disclaimer: never use this code in production. It was created for fun.

Breakdown

Let's break down the solution and comment on some complex or interesting things.

0:04 - starting with HTML template. Game field will be represented with table and each card will be a td element.
As in other similar projects the game field is generated dynamically - iterating height times by width times.
Few variables are defined - width and height represent dimensions of game field, pairs is a number of pairs that should be opened. Important fact - at least one of width or height should be even.
cells 2-dimensional array will store the following infrmation for each cell (or card):

0:55 - define styles. Not inventing anything complex - assume that every card will be a 75x100px block.
At this point you can see first results in the browser.

1:15 - implement function to open a card - openCard.
Goal of this function is replace grey background with something colorful and display a number in the card.
Interesting part is how to calculate background colors. As in other projects we are using hsl color model here using the following expression:
e.setAttribute('style', 'background-color: hsl(' + (320 * c.value / pairs) + ', 100%, 50%)');
Where c.value is number shown on the card (e.g. 1, 2 or 3) and pairs is number of pairs in the game.
In general, gradient looks like this:

Using this gradient one can calculate color of the card.
For example, if there are 3 cards in the game, then the following colors will be used:

For 5 cards the colors will be different:

You can see that the colors are different enough to give extra help for memorisation of cards' position.

1:38 - closeCard function does the opposite - it removes text from the card and changes background to grey (by removing style attribute from HTML element).

1:49 - each game starts with shuffling of the cards.
shuffle function starts with generating an array called cards containing pairs * 2 cards.
Then for each cell of the game field we pick a random element of cards array and store it as value of cells array.
Also we should not forget that the game starts with opening all cards - we need to call openCard for each cell.
After this step our game field will be shuffled.

2:30 - however after game is started we need to wait for a couple of seconds and close all cards.
For that we need closeAllCards function that will just close all cards one by one.
And we schedule execution of this function after 2 seconds using setTimeout function.

2:52 - next step is to handle clicks on the cards.
We need to add a click event handler to each table's cell.
select function contains the logics and handles the following scenarios:

3:45 - last part is to check if the game is over or not.
The criteria is very simple - game is not over if there is at least one open card.
If there are no closed cards left - we consider it as won game. We show a Game over message and restart the game.
This concludes the implementation and we can check results in the browser.

As you see the game is very simple and there are few things that you can improve:

Resources

Sources: https://github.com/5minute/examples/tree/main/memo

See live results:https://jsfiddle.net/0vayk7p4/