A Basic Memory Game with jQuery and PHP
So, I got up this morning to find that my 3.5 year old daughter is busy playing on my laptop. She decided she wants to play so she found the CD for her game, opened the DVD drive and loaded her game. She was very busy playing when I got up, and needless to say I was quite surprised. I figured it was time to create some new games for her, so I thought I’d start with a basic Memory game (some may know it as Concentration). You know, the one with lots of face down cards where you need to find pairs of matching cards.
So let’s break down what’s in this game:
- Pairs of cards
- Raising the level increases the number of cards
- In each turn the player flips over 2 cards
- Matching cards are removed from the board
- The player wins when there are no more cards
- To keep things interesting – some effects
We have a lot to cover in this little guide so in order to keep things as short and quick as we can I’m going to skip some of the phases in building this game. If you want to see those phases, they can all be found in the demo.
To understand this guide you need to know some basic javascript & jQuery, and some PHP. There are some excellent guides for these topics all over the web to get you started or answer your questions.
1. Pairs of cards
In order to create the cards that will be used in the game I used an array to hold the available image files. I used 20 different image files in order to create a randomness factor for the images (assuming I will use less than 40 cards). When the board needs to be initialized, we select some random elements from the array of image files according to the number of cards we need on the board, and then we create card objects from each file. Since we know we need two of each card on the board, we “double” the array by merging it with itself. And now, a quick shuffle and the array for the board is done.
// we won't pick the same ones every time
shuffle($card_files);
// Get the card objects
$cards = array();
for ( $i = 0; $i < $num_of_cards; ++$i ){
$cards[$i] = new Card($card_files[$i]);
$this->css[] = $cards[$i]->get_css_block();
}
// Double the array so that we'll have pairs
$this->cards = array_merge($cards, $cards);
// Shuffle the cards to create their order on the board
shuffle($this->cards);
What is a card? Well a card will be displayed in the game, we will need to know how to build its css properties in such a way that we will have an element with the right background image and so on. As you can see below, the Card class is quite simple:
private $css_class = "";
private $url = "";
function __construct($url) {
$this->url = $url;
$this->css_class = $this->extract_name($url);
}
function get_name(){
return $this->css_class;
}
function get_css_block(){
return "n.".$this->get_name()."{background:url(".$this->url.") center center no-repeat;}";
}
function get_html_simple_block(){
return "r<div class="card {toggle:'".$this->get_name()."'}"></div>";
}
function get_html_block(){
return "r<div class="card {toggle:'".$this->get_name()."'}">
r<div class="off"></div>
r<div class="on"></div>
</div>";
}
private function extract_name($str){
$tmp = pathinfo($str);
return $tmp['filename'];
}
}
The Card object creates its css class name based on the file name. It handles the its own html markup and css markup, and that’s about it. The Board class that we saw pieces from earlier will handle the building of the board’s html and css using the Card class.
2. Raising the level increases the number of cards
In the code above we used a variable called num_of_cards. This variable’s value is determined by the level of the current game: At low levels we will have relatively few cards, but as the level increases, so does the number of cards on the board.
Let’s look at the code for this:
This code was also taken from the Board class. Let’s see how it adds up by taking a look at how we initialize the Board class and set the level:
If the user sends a request for a certain level, we use it for every game he plays from this moment on. If no request is sent, we use the default first level (1). In addition, we can see that we only set the board once per session, unless the a level request is sent, which is almost true (later on we will see that we also reset the board after the player wins). Saving the board in the session is done with the future in mind: as you will see in this post, the logic of matching the cards is carried out, for the moment, by the client. But if you want the game’s logic to be more secure, it should be carried out in the server, and then the server will need to know how the game board is built. For now, let’s go back to our subject.
Just before we move on to see how the player turns the cards we need to actually see how we build the board:
3. In each turn the player flips over 2 cards
Now that we have our board, it’s time to add some interactivity and the main gameplay. We will use a special (and excellent) jQuery plugin to handle the flipping for us. You may have noticed before that, when we built the card’s html block, we actually built a wrapper and two elements inside; one for the back side and one for the front. This markup is recognized by the QuickFlip plugin (which you can get here). Now there are two things we need to do. First, we need to register the Click events for the cards. Second, when a card is clicked, we need to trigger the flip and run a check to see if:
- The two cards match, but there are still cards on the board
- The two cards don’t match
- The two cards match, and there are no cards left on the board (i.e., the player has finished the game)
Let’s take a look:
$(document).ready(function(){
$(".card").bind("click", toggleCard);
$(".card").quickFlip();
});
function toggleCard(event){
if ( chk_cards_timeout != null ){
clearTimeout(chk_cards_timeout);
chk_cards_timeout = null;
checkCards();
}
var $card = $(this);
if($card.children(".off").is(":visible")){
$(document).trigger("flipping_cards");
var num_already_opened = $card.parent("#game_board").find(".card>.on:visible").length;
var css_class = $card.metadata()["toggle"];
$card.children(".on").addClass(css_class);
$card.quickFlipper();
if ( num_already_opened == 1 ){
chk_cards_timeout = setTimeout(checkCards, 1000);
}
}
$card = null;
};
In order to do its job, QuickFlip will duplicate our elements, so we want our selectors to be more precise. That’s why we’re using the direct child (>) directive in our css selector. We can also see the use of the setTimeout function since in case we have two cards turned over, we want to give the user a second or two to look at them before we turn them face down again. We added the card’s css class to its element according to the metadata which we got when the Board class built the html (metadata is another excellent jquery plugin that can be found here).
If you noticed the line: $(document).trigger(“flipping_cards”); we are also triggering a custom event which we can bind to later with different plugins for the game if we will want to, if you haven’t already done so you can read about custom events in my previous post: jQuery custom events.
4. Matching cards are removed from the board
Now that the player can flip a card to see its image, we need to check if we have a match or not. If we have a match, the cards will be removed from the board; if we don’t, the cards will be turned face down again.
$(document).bind("found_match", matchingCards);
$(document).bind("no_match", resetOnCards);
});
function checkCards(){
$on_cards = $("#game_board .card>.on:visible");
if ( $on_cards.length == 2 ){
$(document).trigger("player_made_move");
// Get the first object css class
var css_class = $on_cards.parent(".card").metadata()["toggle"];
$matched_cards = $on_cards.filter("."+css_class);
var event_name = "no_match";
if ( $matched_cards.length == 2 ){
event_name = "found_match";
}
$(document).trigger(event_name, {css_class: css_class});
$matched_cards = null;
}
clearTimeout(chk_cards_timeout);
chk_cards_timeout = null;
$on_cards = null;
};
function resetOnCards(event){
$cards = $(".on:visible");
$.each($cards, function(index, card){
$card = $(card);
var css_class = $card.parent(".card").metadata()["toggle"];
$card.trigger("card_closing");
$card.removeClass(css_class);
$card.parent(".card").quickFlipper();
$card = null;
});
$cards = null;
};
function matchingCards(event, params){
$cards = $("."+params.css_class+".on:visible");
$.each($cards, function(index, card){
var $card = $(card);
$card.trigger("card_removed");
$card.parent(".card").unbind("*").before("<div class='card_ph'></div>").remove();
$card = null;
});
$cards_left = $("#game_board>.card");
if ( $cards_left.length == 0 ){
$(document).trigger("game_won", {});
/*
* quickFlip has a problem when working in IE: when the last
* bound element is removed, a problem which is caused by the
* bound resize event on the window causes
* the end game to get stuck when the game is over...
*/
$(window).unbind("resize");
}
$cards_left = $cards = null;
};
5. The player wins when there are no more cards
In the above code we check if no more cards are left; if that is the case, the player_won event is triggered. When this event fires we need to do several things: We need to notify the server that the player won, and we need to show the player a message letting him know that he won. Let’s see how it goes:
$(document).bind("game_won", gameWon);
});
function gameWon(){
$.getJSON(document.location.href, {won: 1}, notifiedServerWin);
var $game_board = $("#game_board");
var $player_won = $("#player_won");
$game_board.hide();
$player_won.show();
$game_board = $player_won = null;
};
function notifiedServerWin(data){
$("#start_again").show();
}
We can see that we notify the server with a simple ajax request. The server will then reset the board for us and remember the win.
// if we won we need to reset the game board
unset($_SESSION['board']);
$_SESSION['games_won'] = ++$_SESSION['games_won'];
$response = array("status" => "ok");
exit(json_encode($response));
}
6. To keep things interesting β some effects
At this point the game is basically working, and it’s time to add some sound to it. Usually I absolutely hate sounds in web pages but since this is a game, it’s allowed :). So in order to add the sound, we are going to use the “hooks” we left in the code in the shape of custom events. We are going to use a Flash object with some preloaded mp3s for the sound effects. The communication between the JavaScript and the Flash (actionscript) is pretty simple these days, but it’s not in the scope of this guide. However, if you want a guide on this let me know in the comments and I’ll post one. For the purpose of this guide, let’s assume we have a Flash object that can accept commands and output sounds. We will use swfobject to embed the flash object to the page.
<script type="text/javascript">
var flashvars = false;
var attributes = {};
var params = {
allowscriptaccess : "always",
wmode : "transparent",
menu: "false"
};
swfobject.embedSWF("sfx.swf", "sfx_movie", "1", "1", "9.0.0",
"expressInstall.swf", flashvars, params, attributes);
</script>
Now that’s done, it time to handle the binding the events and triggering the sounds. We want the sound to play when the player flips a card over, when we wins the game, and when a match is found.
$(document).bind("flipping_cards.sound", playFlip);
$(document).bind("game_won.sound", playCheer);
$(document).bind("found_match", playMatch);
});
function playCheer(){
var sfxMovie=getFlashMovieObject("sfx_movie");
try{
sfxMovie.cheer();
} catch(e) {};
};
function playFlip(){
var sfxMovie=getFlashMovieObject("sfx_movie");
try{
sfxMovie.flip();
} catch(e) {};
};
function playMatch(){
var sfxMovie=getFlashMovieObject("sfx_movie");
try{
sfxMovie.match();
} catch(e) {};
};
That’s it! You can check how everything fits in place and view all the code by looking at the demo.
So until next time, have fun!
Adi Gabai
32 Responses to A Basic Memory Game with jQuery and PHP
Excellent Entry, I am going to implement this on my website if you don’t mind π
Thanks for the script!
Hello. I was just surfing for fun and then I found your web site. Fine article. I find these details useful. Thank you ! I will thank the person who told me to visit your blog.
Hello,
I can hear the sounds of the memory game when I go to the demo, but there is no sound when I play the downloaded version of the game (I did download dfwsoft and placed its folder inside the game folder ). Would you be so kind as to tell me what have I done wrong I and how to rectify it?
Thank you so much!
Mari
Hi Mari,
I verified that the download package is valid and can play sound properly, I’m guessing the issue you are having is local. Please try to download the demo and extract the files into a folder under your web server; you don’t need to download anything else to make it work as it should.
If things are still not working, I may need to see a sample of your installation or get some details regarding your environment. Such as:
In what browser and version have you tried it?
Have you made any modifications?
Do you see any loading issues or JavaScript errors?
Have you tried running one of the sound functions manually in a JavaScript console (like playCheer() )? What were the results?
Are you running from localhost or from another server?
What is your adobe flash plug-in version?
Best Regards,
Adi
Thank you so much for your reply! It is working now. If I wanted to change the sounds, where do I have to go to replace the files.
Thank you!!
Mari
Hi Mari,
In the current version you will need to replace the swf file and put your own. due to licensing issues I haven’t created the sound files as external. If you need help creating a new swf with the sounds email me and I’ll try to help.
Best Regards,
Adi
Hi Adi,
I’m really sorry to bother you, but I would very much appreciate it if you could let me know how to create a new swf to replace the current sounds ( I tried replacing the sfx.swf but could not make it work.
Kind regards,
Mari
P.S. I could not find your e-mail address.
Hello Adi
impresive work! I was trying to contact you but could not find your email address.
I would like to use your scripts in a web site but I did not find any Creative commons nor a statement in regards copyrights. It would be really nice if you could give me some info about this.
thanks in advance for a short notice π
Regards
blady
Hi blady,
Iβm glad you like it, there are more coming soonβ¦ (as soon as I’ll have some time to arrange them)
You may use any script on this site without any terms, though a link back here would be most appreciated.
Best Regards,
Adi
I have down loaded the file. How do I place it in my WordPress/Builder Site? I plan to revise all the images.Are there any steps I need to take to make it work on my site?
Hi Adi, first thanks for that great game! I love it so much that I’ve built it into a customers’ website (http://tinyurl.com/5w86cgh).
Well and today when the website went “live” we recognized a real big problem: if you click the cards in a quick sequence/speed, they all remain visible!! We tested that on Mac and PC, Firefox and IE … all the same. Well, your own demo also.
Any ideas? It would be too bad if we have to remove the game as we all like it so much.
Best Regards,
Thorsten
Hi Thorsten,
Please take in mind that the idea behind this game / post was more educational and not something polished for production use.
While this is probably not relevant for you anymore I will still work on a solution to this problem since it might effect others.
And handling timing issues can be tough. I hope to have something posted about it soon.
If you solved it by and can share the solution I’ll be happy to hear from you π
Best Regards,
Adi
Any chance you have a new version of this coming out any time soon? It’s pretty awesome.
Hi Scott,
Sorry it took so long to reply, yes I am working on a new version but it won’t come out any time soon I’m afraid
Best Regards,
Adi
Here is same script with some cool effects
http://www.amitpatil.me/image-match-javascript-memory-game/
Excellent game, i was more loocking for a slot machine jquery plugin=) but this is very interesting too
Is it possible to pass the total moves with php?
I would like to develop a game for my website where scores could be posted.
Thx in advance!
Hi Adi!
I was stubling about the web trying to find sample code to make such a fun and simple game and I came across yours! Thank you!
I’m wondering how I would go about adding a scoring element including clicks and time. I’m just trying to get my feet wet with developing, and think that this would be a good way to get some practical learning in. I know it will have to do with a timer trigger that starts with the first game play click, but I don’t know how to element that. Scoring would be a simple (Number of clicks * seconds) subtracted from an arbitrary number for each level. Also, adding a couple of ‘special features’ card tiles would be cool, like a ‘Next Card Stays Hidden’ where the next card you click would stay revealed while you searched for it’s match, or a ‘next pair deleted’ card that would delete the next matched set you clicked on.
Anyway, if there’s anywhere you can point me, that would be great!
Rich
Thanks for this tutorial Adi. I’m going to try to implement it on a childrens website I’m working on at the moment.
Hello Adi,
thank you for the memory game. I would like to use it on my website – but I am thinking of matching two different cards (headword and its translation for example). I create dynamically images from text and saving them to tmp directory. The pairs are named this way – ./images/tmp/file-4-1.png and ./images/tmp/file-4-2.png
How can I acheive now that these two picture match? Thank you Ales
Thanks for this great tutorial. I am also looking for a solution of the problem Thorsten and Richard lately wrote about: you can click onto every picture very fast and open it.
Did you find any solution?
Hi Adi,
If you click the second tile before the first tile is finished rotating, both tiles remain visible π
good one sample card game. Thank you.
Thanks, it’s simple and very useful for beginners who want to start game programming. Here is my HTML5 and jquery powered game with box2dweb at : http://pixsansar.com/cool-box2dweb-games
Click on another card, while the effect is still going on on the first card, or double click on any card, and see what happens. I had this same situation happen, when I made a tic-tac-toe game, so i had to make tiles .fade()/.fadeTo(), instead of .effect(“explode”… .
Please does anyone know how to display the name of the matched images when they match (right before or right after they disappear)?
Great tutorial. Thank you!
Thanks dear your game is so fantastic and learn the code for new game.