Mahjong Game – Adding Features

Though it’s been a while, I think the time has come to revisit one of my previous tutorials: the Mahjong game. For those of you that haven’t read it, the Mahjong tutorial went throughthe stages of creating a very basic Mahjong game: the graphics, the layouts, the boards and all the rest. Since I published this series of tutorials, I’ve been asked about various of features such as hint and shuffle, and have decided that it’s about time for me to add them… so here goes 🙂

Argh! Where is that last pair?

Adding hints to the game is a fairly easy task, since we already have the ability to check which blocks are free. Even better: because we’ve marked the free blocks with a special class name, retrieving them at a later stage is just a matter of using a selector. Right now you may be thinking that showing a hint is a bit more complicated than just finding the free blocks; after all, a move requires two matching blocks. And you’re right, there is a bit more to it than that. Let’s see how we can do it:

function showHint(event){
    event.preventDefault();
    // Get the free blocks
    var $blocks = $("#game_board .block.free");
    var foundMatch = false;
    for(var x=0,total=$blocks.length;x<total && !foundMatch;++x){
        $block = $($blocks.get(x));
        var class_name = ($block.metadata()["single"] ?
                         $block.metadata()["group_class"]                                  
                        : $block.metadata()["block_class"]);
        // Check if this block class has at least two free blocks
        $match = $("#game_board .block.free."+class_name);
        if ( $match.length > 1 ){
            $match.filter(":first,:last").addClass("hint");
            foundMatch = true;
        }
        $block = $match = null;
    }
    $blocks = null;
    return false;
};

What we’ve done here is checked each free block on the board to see if there are more free blocks of the same class. The logic here is very basic: there are no priorities, nor any forward planning. This means that while this method will always find a free pair if one exists, it will by no means guarantee a “win” for the player.

If I could just move things around a bit…

The next feature we’re going to look at is shuffling the board. In order to shuffle the board we need to change the locations of all the blocks while maintaining the occupied spots in the layout, otherwise things could get a bit weird. But like the hint feature, it’s a bit more complicated then that. If you followed the previous posts you may remember that each block remembers its index in the layout, its layer, whether it’s covered or not, its z-index, and some more attributes, some of which help us determine whether the block is free. So in order not to mess up everything, we need to separate the position spot attributes (layer, index, z-index, top, left, covered) from the block attributes. Let’s see how we can do that:

function shuffleBoard(event){
    event.preventDefault();
    var spots = [];
    // Get all the blocks
    var $blocks = $("#game_board .block");
    $.each($blocks, function(index, el){
        // Save the used position so we can maintain the layout
        $block = $(el);
        var layer = $block.metadata()['layer'];
        var index = $block.metadata()['index'];
        spots.push({css: {
            top: $block.css("top"),
            left: $block.css("left"),
            "z-index": $block.css("z-index")
        }, layer: layer, index: index, covered: $block.metadata()['covered']});
        $block.removeClass('layer_'+layer).removeClass('index_'+index);
        $block = null;
    });
    // Now shuffle the positions and return the blocks
    spots = shuffle(spots);
    // Place the blocks again
    $.each($blocks, function(index, el){
        $block = $(el);
        var spotInfo = spots.pop();
        $block.css(spotInfo.css);
        // Alter the block's saved metadata to match the new position
        var mdata = $block.metadata();
        mdata['index'] = spotInfo.index;
        mdata['layer'] = spotInfo.layer;
        mdata['covered'] = spotInfo.covered;
        $block.data("metadata", mdata);
        $block.addClass('layer_'+spotInfo.layer).addClass('index_'+spotInfo.index);
        $block = null;
    });
   
    // Check for free blocks again, since we changed everything
    $blocks.removeClass("free").removeClass("hint");
    $(document).trigger("changed_num_blocks");
   
    $blocks = null;
    return false;
}

Thats about it: get all the blocks, extract their position information, and shuffle the positions array so we can put the blocks in a different order. After that, since we changed everything, we need to determine which blocks are free in the new order and inform the user of the number of pairs available.

A new look

They say you can’t have a new version without some graphical makeover, so I apologize that the game looks the same… but I do have a new, beetle-shaped layout for you, courtesy of Maurice de Regt. So head over to the demo and see how it looks :).

Some more code is required to get the finishing touches and interconnect all the features, but I do believe we’ve got the core of it covered. As always, you can find the rest of the javascript in the game demo itself:

Zip file for this tutorial

Demo for this tutorial

So until next time, have fun!

Adi Gabai

4 Responses to Mahjong Game – Adding Features

Leave a Reply

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

Categories