Guide
du lecteur

Jeu du Taquin en jQuery sur vos sites !

par
le à 09h02
Démonstration du jeu du taquin

Vous connaissez probablement ce jeu ou l'on mélange une image composée de 4 fois 4 carrés dans le seul but de la remettre en ordre une fois celle-ci mélangée. Il peut s'agir de la reconstituer de mémoire ou à partir d'un modèle. Ce jeu c'est le Taquin et je vous le propose sous la forme d'un plugin jQuery pour votre site.

Démonstration du plugin

En jouant avec les paramètres ci-dessous vous pourrez construire le Taquin qui vous intéresse. Vous n'aurez plus qu'à l'inclure dans votre site. Bien entendu, pour les développeurs, le détail du plugin est fait plus loin dans cet article.

Paramètres

Image Source
Adresse relative ou absolue autorisée.
px Laissez 0 pour la dimention originale.
px Laissez 0 pour la dimention originale.
Propriétés
Le mimnimum possible est 2.
Valeur possible : tl, tr, bl, br.
Nombre de déplacement pour mélange.
Validation

Aperçu

Cliquer sur l'image ci-dessous pour voir le résultat après mélange.

Installation

Si vous n'êtes pas un expert en JavaScript, vous pouvez paramétrer le jeu ci-dessus (dans "Aperçu") avec les éléments dans "Paramètres" et inclure les codes ci-dessous dans votre site.

  • Il faut auparavant avoir inclus le script jQuery dans sa page.
  • Le chemin du plugin est celui où vous avez placé le script sur votre site.
Script à inclure en pied de Body
En supposant que votre fichier soit accessible à l'adresse ./jquery/plugin/
HTML à inclure n'importe ou dans le Body

Téléchargement

Code source du fichier Plugin JQuery Game Taquin

Vous trouverez ce code à http://www.haeresis.fr/framework/jquery/plugin/jquery.game-taquin.js

sinon

Copiez-collez le code ci-dessus dans un fichier appelé par exemple jquery.game-taquin.js :

////////////////////////////////////////////
// Jeu du Taquin en jQuery sur vos sites. //
//////////////////////////////////////////////
// Auteur : Bruno Lesieur - www.haeresis.fr //
//////////////////////////////////////////////
(function ($) {
    "use strict";

    // ==Description==
    // Pour chaque élément 'fn' :
    // Créé un nouvel élément juste après l'image cible composée de <div> en absolute. 
    // Un clique permet de mélanger le jeu.
    // La résolution du tableau remet les éléments aux états initiaux.

    // ==image==
    // Ceci réprésente l'adresse de l'image qui sera fractionnée pour être jouée.

    // ==params==
    // width : Indique la taille que fait l'image en largeur. Si rien ou zéro est précisé, la taille par défaut est celle de l'image.
    // height : Indique la taille que fait l'image en hauteur. Si rien ou zéro est précisé, la taille par défaut est celle de l'image.
    // division : Indique le nombre de carré en largeur/hauteur qui compose le taquin.
    // hidePart : Indique quelle partie du carré est masquée pour faire office de trou pour les déplacements. Les valeurs possibles sont Haut Gauche "tl", Haut droite "tr", Bas gauche "bl", Bas droite "br" (par défaut).
    // mixing : Indique le nombre de déplacement fait aléatoirement pour simuler un mélange à la main du taquin.
    // success : Si écrasé par une fonction, exécute cette fonction en cas de complétion du taquin. Sinon, renvoi sous chaine de caractère le message passé dans un "alert()".

    $.fn.gameTaquin = function (image, params, success) {

        // Paramètres initiaux écrasés s'ils sont passés en paramètres d'entrées.
        params = $.extend({
            width: 0,
            height: 0,
            division: 4,
            hidePart: "br",
            mixing: 200,
            success: "Complete"
        }, params);

        // Si la division est inférieur à 2, la valeur est 2.
        if (params.division < 2) { params.division = 2; }

        // Fonction éxécutée par défaut en cas de succès.
        if (!$.isFunction(params.success)) {
            var temp = params.success;
            params.success = function () {
                alert(temp);
            };
        }

        /***********************************************/
        /** Initialisation des variables et fonctions **/
        /***********************************************/

        var clickFunction,
            gameStart,
            gameState,
            squareWidth = 0,
            squareHeight = 0;

        // Créer les états de jeu.
        function initialiseGameVar(gameDivision) {
            var game = [],
                gameRow,
                gameDiv,
                xi = 0,
                yi = 0,
                currentDisplay;

            for (xi = 0; xi < gameDivision; xi += 1) {
                gameRow = [];
                for (yi = 0; yi < gameDivision; yi += 1) {
                    gameDiv = [];

                    currentDisplay = true;
                    if ((xi === 0) && (yi === 0) && (params.hidePart === "tl")) { currentDisplay = false; }
                    if ((xi === (gameDivision - 1)) && (yi === 0) && (params.hidePart === "tr")) { currentDisplay = false; }
                    if ((xi === 0) && (yi === (gameDivision - 1)) && (params.hidePart === "bl")) { currentDisplay = false; }
                    if ((xi === (gameDivision - 1)) && (yi === (gameDivision - 1)) && (params.hidePart === "br")) { currentDisplay = false; }

                    gameDiv[0] = currentDisplay;
                    gameDiv[1] = "taquin-" + xi + "-" + yi;
                    gameRow[yi] = gameDiv;
                }
                game[xi] = gameRow;
            }

            return game;
        }

        // Repéré la case dans le jeu.
        function getId(taquin) {
            var classList = taquin.attr("class").split(/\s+/),
                result = "";

            $.each(classList, function (index, item) {
                if (index === 0) {
                    result = item;
                }
            });

            return result;
        }

        function getCoord(id) {
            var coord = [],
                xi = 0,
                yi = 0;

            for (xi = 0; xi < gameStart.length; xi += 1) {
                for (yi = 0; yi < gameStart[0].length; yi += 1) {
                    if (gameState[xi][yi][1] === id) {
                        coord[0] = xi;
                        coord[1] = yi;
                    }
                }
            }

            return coord;
        }

        // Repéré l'élément vide.
        function getEmpty() {
            var empty = [],
                xi = 0,
                yi = 0;

            for (xi = 0; xi < gameStart.length; xi += 1) {
                for (yi = 0; yi < gameStart[0].length; yi += 1) {
                    if (gameState[xi][yi][0] === false) {
                        empty[0] = xi;
                        empty[1] = yi;
                        empty[2] = gameState[xi][yi][1];
                    }
                }
            }

            return empty;
        }

        // vérifier si un élément peut être bougé.
        function getIsMovable(x, y) {
            var isMovable = false;

            try {
                if (gameState[x][y - 1][0] === false) { isMovable = true; }
            } catch (err1) {}
            try {
                if (gameState[x + 1][y][0] === false) { isMovable = true; }
            } catch (err2) {}
            try {
                if (gameState[x][y + 1][0] === false) { isMovable = true; }
            } catch (err3) {}
            try {
                if (gameState[x - 1][y][0] === false) { isMovable = true; }
            } catch (err4) {}

            return isMovable;
        }

        // Mélange pièce.
        function randomGame(mixing) {
            var empty,
                possibleMove,
                temp,
                i = 0,
                j = 0,
                xi = 0,
                yi = 0,
                xj = 0,
                yj = 0,
                rand = 0,
                newPosTop = 0,
                newPosLeft = 0;

            for (j = 0; j < mixing; j += 1) {
                empty = getEmpty();
                possibleMove = [];
                i = 0;

                // On cherche les voisins déplaçable.
                try { if (gameState[empty[0]][empty[1] - 1][0] === true) {
                    possibleMove[i] = [];
                    possibleMove[i][0] = empty[0];
                    possibleMove[i][1] = empty[1] - 1;
                    i += 1;
                } } catch (err1) {}
                try { if (gameState[empty[0] + 1][empty[1]][0] === true) {
                    possibleMove[i] = [];
                    possibleMove[i][0] = empty[0] + 1;
                    possibleMove[i][1] = empty[1];
                    i += 1;
                } } catch (err2) {}
                try { if (gameState[empty[0]][empty[1] + 1][0] === true) {
                    possibleMove[i] = [];
                    possibleMove[i][0] = empty[0];
                    possibleMove[i][1] = empty[1] + 1;
                    i += 1;
                } } catch (err3) {}
                try { if (gameState[empty[0] - 1][empty[1]][0] === true) {
                    possibleMove[i] = [];
                    possibleMove[i][0] = empty[0] - 1;
                    possibleMove[i][1] = empty[1];
                    i += 1;
                } } catch (err4) {}

                // On choisit au hasard le voisin à interchanger.
                rand = Math.floor(Math.random() * possibleMove.length);

                // On l'interchange dans l'état de jeu.
                temp = gameState[empty[0]][empty[1]];
                gameState[empty[0]][empty[1]] = gameState[possibleMove[rand][0]][possibleMove[rand][1]];
                gameState[possibleMove[rand][0]][possibleMove[rand][1]] = temp;
            }

            // On trouve les déplacements pour l'animation.
            // Pour chaque élément positionné au départ.
            for (xi = 0; xi < gameStart.length; xi += 1) {
                for (yi = 0; yi < gameStart[0].length; yi += 1) {
                    // On cherche sa nouvelle position
                    for (xj = 0; xj < gameState.length; xj += 1) {
                        for (yj = 0; yj < gameState[0].length; yj += 1) {
                            // On trouve la nouvelle position.
                            if (gameStart[xi][yi][1] === gameState[xj][yj][1]) {
                                // On récupère les coordonnées de la nouvelle position.
                                if (gameStart[xi][yi][0] === true) {
                                    newPosTop = parseInt((squareHeight * yj) - $(".taquin-" + xi + "-" + yi).position().top, 10) + "px";
                                    newPosLeft = parseInt((squareWidth * xj) - $(".taquin-" + xi + "-" + yi).position().left, 10) + "px";

                                    $(".taquin-" + xi + "-" + yi).animate({
                                        top: "+=" + newPosTop,
                                        left: "+=" + newPosLeft
                                    });
                                } else {
                                    $(".taquin-" + xi + "-" + yi).css({
                                        "top": (squareHeight * yj),
                                        "left": (squareWidth * xj)
                                    });
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

        // Démarrer le jeu
        function launcher(generate) {
            // On enlève l'intialisation de jeu.
            generate.find(".taquin-part").unbind("click");

            // On cherche l'élément vide.
            var empty = getEmpty();

            $("." + empty[2]).animate({ opacity: "0" }, function () {
                // On le cache.
                $(this).hide();

                // On mélange le jeu.
                randomGame(params.mixing);

                // Annules toutes les animations s'il y en a de pas finie.
                generate.find(".taquin-part").clearQueue();

                // Permettre le déplacement des pièces, jeu jouable !
                generate.find(".taquin-part").bind("click", function () {
                    clickFunction(generate, $(this));
                });
            });
        }

        // Quand on clique sur une partie après le lancement du jeu.
        clickFunction = function (generate, source) { // rempli ici mais défini plus haut car appelé dans la fonction d'au dessus.
            var empty,
                moveTop = 0,
                moveLeft = 0,
                tempTop = 0,
                tempLeft = 0,
                temp,

            // Identifier l'élément.
                id = getId(source),

            // Trouver sa position dans le jeu.
                coord = getCoord(id),

            // Vérifier si il est déplaçable.
                isMovable = getIsMovable(coord[0], coord[1]);

            // Si il est déplaçable, interchanger les positions.
            if (isMovable) {
                // Trouver l'élément vide.
                empty = getEmpty();

                // On trouve les déplacements pour l'animation.
                generate.find("." + empty[2]).show();
                moveTop = generate.find("." + id).position().top - generate.find("." + empty[2]).position().top;
                moveLeft = generate.find("." + id).position().left - generate.find("." + empty[2]).position().left;
                generate.find("." + empty[2]).hide();

                //On met les variable en temporaire pour l'interchangement.
                tempTop = source.position().top;
                tempLeft = source.position().left;

                // On inverse les positions de l'éléments vide.
                temp = gameState[coord[0]][coord[1]];
                gameState[coord[0]][coord[1]] = gameState[empty[0]][empty[1]];
                gameState[empty[0]][empty[1]] = temp;

                // On anime le changement.
                source.unbind("click").animate({
                    top: "-=" + moveTop,
                    left: "-=" + moveLeft
                }, function () {
                    source.bind("click", function () {
                        clickFunction(generate, source);
                    });

                    // Si l'état initial après animation est le même que l'état actuel, on a fini le jeu.
                    if (gameState.toString() === gameStart.toString()) {
                        // On enlève les fonctions de déplacement.
                        generate.find(".taquin-part").unbind("click");

                        // On cherche l'élément vide pour le ré-afficher.
                        var empty = getEmpty();
                        $("." + empty[2]).show().animate({ opacity: "1" }, function () {
                            // On replace le mécanisme de démarrage du jeu.
                            generate.find(".taquin-part").bind("click", function () {
                                launcher(generate);
                            });
                            // On exécute la fonction de réussite.
                            params.success();
                        });
                    }
                });

                generate.find("." + empty[2]).css({
                    "top": tempTop + "px",
                    "left": tempLeft + "px"
                });
            }
        };

        // Fonction exécutée sur chaque élément sélectionné comme model pour devenir un taquin.
        function taquin(generate, imageOriginalSize) {
            var gameDivision = params.division,
                imageWidth = 0,
                imageHeight = 0,
                xi = 0,
                yi = 0,
                backgroundSizeString;

            // Récupère la largeur et hauteur des partis du taquin.
            if (parseInt(params.width, 10) !== 0) { imageWidth = params.width; } else { imageWidth = imageOriginalSize[0]; }
            if (parseInt(params.height, 10) !== 0) { imageHeight = params.height; } else { imageHeight = imageOriginalSize[1]; }
            squareWidth = Math.round(imageWidth / gameDivision);
            squareHeight = Math.round(imageHeight / gameDivision);

            // Initialise l'état initial et actuelle du jeu.
            gameStart = initialiseGameVar(gameDivision);
            gameState = initialiseGameVar(gameDivision);

            generate.css({
                "position": "relative",
                "width": imageWidth + "px",
                "height": imageHeight + "px"
            });

            // Générer les partis du taquin.
            for (xi = 0; xi < gameStart.length; xi += 1) {
                for (yi = 0; yi < gameStart[0].length; yi += 1) {
                    backgroundSizeString = ((params.width != 0) ? params.width : imageOriginalSize[0]) + "px " + ((params.height != 0) ? params.height : imageOriginalSize[1]) + "px";
                    $("<div>", {
                        css: {
                            cursor: "pointer",
                            backgroundImage: "url('" + image + "')",
                            backgroundPosition : "-" + parseInt(squareWidth * xi, 10) + "px -" + parseInt(squareHeight * yi, 10) + "px",
                            "background-size" : backgroundSizeString,
                            position: "absolute",
                            top: parseInt(squareHeight * yi, 10) + "px",
                            left: parseInt(squareWidth * xi, 10) + "px",
                            width: squareWidth + "px",
                            height: squareHeight + "px"
                        }
                    }).appendTo(generate).addClass(gameStart[xi][yi][1]).addClass("taquin-part");
                }
            }

            // Permettre de démarrer le jeu.
            generate.find(".taquin-part").bind("click", function () {
                launcher(generate);
            });
        }

        // S'execute pour chaque élément 'fn' trouvé.
        return this.each(function () {

            // Information sur l'image.
            var waitForImageSize,
                forImage = new Image(),
                element = $(this),
                imageSize = [];

            forImage.src = image;

            // Quand on obtient une taille pour l'image, on execute le mécanisme.
            waitForImageSize = setInterval(function () {
                if (forImage.width !== 0) {
                    imageSize[0] = forImage.width;
                    imageSize[1] = forImage.height;

                    // Génère l'élément qui contiendra les partis du taquin.
                    var generate = $("<div>").addClass("taquin-generate");
                    if (element.next().hasClass("taquin-generate")) {
                        element.next().remove();
                    }
                    element.after(generate);

                    taquin(generate, imageSize);
                    clearInterval(waitForImageSize);
                }
            }, 50);
        });
    };
}(jQuery));
        

Documentation

Utilisation

La fonction de base s'utilise comme suit :

  • $(dom_element).gameTaquin(image);

Exemple sur l'élément de DOM suivant <div id="taquin"></div>.

$("#taquin").gameTaquin("http://blog.haeresis.fr/upload/image/image-taquin.jpg");

image

Ceci réprésente l'adresse de l'image qui sera fractionnée pour être jouée.

Paramètres

Les paramètres se donnent sous forme d'objet JSON après l'url de l'image :

  • $(DOM_ELEMENT).gameTaquin(IMAGE_URL, { PARAMETERS... });

Les paramètres sont cumulables comme dans cet exemple.

$("#taquin").gameTaquin(
    "http://blog.haeresis.fr/upload/image/image-taquin.jpg",
    {
        division: 6,
        hidePart: "tl",
        success: "Félicitation !"
    }
);

width

Indique la taille que fait l'image en largeur. Si rien ou zéro est précisé, la taille par défaut est celle de l'image.

height

Indique la taille que fait l'image en hauteur. Si rien ou zéro est précisé, la taille par défaut est celle de l'image.

division

Indique le nombre de carré en largeur/hauteur qui compose le taquin.

hidePart

Indique quelle partie du carré est masquée pour faire office de trou pour les déplacements. Les valeurs possibles sont Haut Gauche "tl", Haut droite "tr", Bas gauche "bl", Bas droite "br" (par défaut).

mixing

Indique le nombre de déplacement fait aléatoirement pour simuler un mélange à la main du taquin.

success

Si écrasé par une fonction, exécute cette fonction en cas de complétion du taquin. Sinon, renvoi sous chaine de caractère le message passé dans un "alert()".

3 Commentaires

Choisir un Avatar depuis une url

Adresse : (64px x 64px)
L'url de votre image est incorrecte !

ou Changer la couleur de fond

Nouvelle couleur !

et Choisir un Avatar dans la liste

ChangerFermer
Je crois que vous avez oublié votre commentaire !
Votre email est bizarre !
L'url de votre site doit ressembler à ça : http(s)://(www.)votresite.ici !
Vous ne pouvez pas laisser d'email en restant Anonyme !
Vous ne pouvez pas laisser de site web sans votre email !

Votre commentaire a été ajouter ci-dessous ! Si vous désirez le supprimer ultérieurement, servez vous du code suivant :

Les commentaires sont actuellement affiché du plus rescent au plus ancien. Vous pouvez inverser l'ordre en cliquant ci-dessous :

  • Du plus rescent au plus ancien
  • Du plus ancien au plus rescent
118
Supprimer
Votre commentaire a été supprimé.
le à 13h47

Bonjour,
Super sujet. j'aimerai que le taquin soit mélangé immédiatement à 'ouverture de la page, sans clic. Comment faire, je n'arrive pas à le faire.
Merci

Je trouve ce commentaire pertinent ?

Mauvais code de suppression
105
Supprimer
Votre commentaire a été supprimé.
le à 20h31

Bonjour,
j'aimerai intégrer 1 jeu de taquin dans 1 page perso contenant une image présélectionné. Je ne comprends pas quelles parties je dois inclure, ni ou dans ma page.
Pouvez-vous m'aider ?

Je trouve ce commentaire pertinent ?

Mauvais code de suppression
75
Supprimer
Votre commentaire a été supprimé.
le à 13h31

Belle démonstration ! Merci pour le code source :p
Tu gères !

Je trouve ce commentaire pertinent ?

Mauvais code de suppression
Supprimer
Votre commentaire a été supprimé.
A l'instant

Je trouve ce commentaire pertinent ?

Vous avez aimé cette article ? Commentez le ou .

A propos de l'auteur

Bonjour, je suis Bruno Lesieur, architecte web basé sur Annecy. Intégrateur HTML5, CSS3 et JavaScript ou encore développeur Front-End ; appelez moi comme vous le voulez mais sachez que je suis très friand de performance web, de Responsive Web Design, d'accessibilité web, de bonnes pratiques, de techniques et d'outils de gestion web, de factorisation et d'automatisation, de partage et casse-tête en tout genre ainsi que de boissons gazeuses.
Article Commentaires Avis 3
jeu-du-taquin-en-jquery-pour-vos-sites

Quelques #IDet beaucoup de .CLASS

Année
Mois
http://blog.haeresis.fr default fr FR Janvier Février Mars Avril Mai Juin Juillet Août Septembre Octobre Novembre Décembre