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:

  1. Pairs of cards
  2. Raising the level increases the number of cards
  3. In each turn the player flips over 2 cards
  4. Matching cards are removed from the board
  5. The player wins when there are no more cards
  6. 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.

// Shuffle the available image files array so  
// 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:

class Card{
    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:

private var $modes = array(6, 8, 10, 12, 15, 18);
...
$num_of_cards = $this->modes[$level - 1];

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:

$level = 1;

if (isset($_REQUEST['level']) ) {
    $level = $_REQUEST['level'];
       
    $board = new Board($level, $CARDS);
    $_SESSION['board'] = $board;
} else {
    if (!isset($_SESSION['board'])) {
        $board = new Board($level, $CARDS);
        $_SESSION['board'] = $board;
    } else {
        $board = $_SESSION['board'];
    }
}

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:

<style>
    <?php
        print $board->get_css();
    ?>
</style>
...
<div id="game_board" style="width:<?php print $board->get_cols()*75; ?>px;">
<?php
    print $board->get_html();
?>
</div>

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:

var chk_cards_timeout = null;

$(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).ready(function(){
    $(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).ready(function(){
    $(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 ( isset($_REQUEST["won"]) ){
    // 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" src="swfobject.js"></script>
<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).ready(function(){
    $(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.

Zip file for this tutorial

Demo for this tutorial

So until next time, have fun!

Adi Gabai

Tags: , ,

32 Responses to “A Basic Memory Game with jQuery and PHP”

  1. Excellent Entry, I am going to implement this on my website if you don’t mind ;)

    Thanks for the script!

  2. Jenette says:

    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.

  3. Mari says:

    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

  4. Adi Gabai says:

    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

  5. Mari says:

    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

  6. Adi Gabai says:

    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

  7. Mari says:

    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.

  8. blady says:

    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

  9. Adi Gabai says:

    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

  10. Kristie says:

    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?

  11. Thorsten says:

    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

    • Adi Gabai says:

      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

  12. Scott says:

    Any chance you have a new version of this coming out any time soon? It’s pretty awesome.

  13. Diablo3 says:

    Excellent game, i was more loocking for a slot machine jquery plugin=) but this is very interesting too

  14. White hot arcade has the bestSkill games…

    [...]Creating a basic memory game with jQuery and php | Webdev Playground[...]…

  15. fun onlinegames…

    [...]Creating a basic memory game with jQuery and php | Webdev Playground[...]…

  16. Bram says:

    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!

  17. oyun says:

    oyun…

    [...]Creating a basic memory game with jQuery and php | Webdev Playground[...]…

  18. 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

Leave a Reply

(English only please)