Exercice pratique : le MASTERMIND

Utilisez la souris pour déplacer les blocs.

Autres exercices AS3
Informations

Voici un petit exercice pour réaliser un Mastermind, à l’attention des débutants. Ne vous attendez pas à un tutorial décrit pas à pas, le but d’un exercice étant que le lecteur fasse l’effort de la compréhension avec toutefois des explications de base.

- Les sources sont disponibles à la fin de l’exercice.
- Retrouvez ce tutoriel sur le Wiki de Mediabox

Partagez
Etude préliminaire

Tout d’abord le MASTERMIND c’est quoi ? (merci Wikipedia)

Le Mastermind est un jeu de société, de réflexion, et de déduction, inventé par Mordecai Meirowitz dans les années 1970. Il se présente généralement sous la forme d’un plateau perforé de 10 rangées de quatre trous pouvant accueillir des pions de couleurs. Le nombre de pions de couleurs différentes est de 8 et les huit couleurs sont généralement : rouge ; jaune ; vert ; bleu ; orange ; blanc ; violet ; fuchsia. Il y a également des pions blancs et rouges (ou noirs) utilisés pour donner des indications à chaque étape du jeu. Il existe de nombreuses variantes suivant le nombre de couleurs, de rangées ou de trous.

Un joueur commence par placer son choix de pions sans qu’ils soient vus de l’autre joueur à l’arrière d’un cache qui les masquera à la vue de celui-ci jusqu’à la fin de la manche. Le joueur qui n’a pas sélectionné les pions doit trouver quels sont les quatre pions, c’est-à-dire leurs couleurs et positions. Pour cela, à chaque tour, le joueur doit se servir de pions pour remplir une rangée selon l’idée qu’il se fait des pions dissimulés.

Une fois les pions placés, l’autre joueur indique :

- le nombre de pions de la bonne couleur bien placés en utilisant le même nombre de pions rouges ; - le nombre de pions de la bonne couleur, mais mal placés, avec les pions blancs.

Il arrive donc surtout en début de partie qu’il ne fasse rien concrètement et qu’il n’ait à dire qu’aucun pion ne correspond, en couleur ou en couleur et position.

La tactique du joueur actif consiste à sélectionner en fonction des coups précédents, couleurs et positions, de manière à obtenir le maximum d’informations de la réponse du partenaire puisque le nombre de propositions est limité par le nombre de rangées de trous du jeu. Dans la plupart des cas, il s’efforce de se rapprocher le plus possible de la solution, compte-tenu des réponses précédentes, mais il peut aussi former une combinaison dans le seul but de vérifier une partie des conclusions des coups précédents et de faire en conséquence la proposition la plus propice à la déduction d’une nouvelle information.

Le joueur gagne cette manche s’il donne la bonne combinaison de pions sur la dernière rangée ou avant.

Dans notre cas nous allons utiliser une variante à 5 pions et 5 couleurs et c’est l’ordinateur qui choisira automatiquement la solution. Etant donné que j’ai toujours eu du mal avec les pions blancs et noirs pour indiquer au joueur la validité de ses combinaisons, je vais les remplacer par du texte, mais le résultat est le même.

Les pré-requis

Je vous recommande fortement d’avoir au moins lu les exercices suivants : DEMINEUR, PONG, SNAKE, TAQUIN
Les exercices sont courts mais de nombreuses astuces y sont proposées, je ne les expliquerai pas à chaque nouvel exercice.

Pour ce programme vous devez connaître :

Variables et types : http://help.adobe.com/fr_FR/FlashPl...
Fonctions et paramètres : http://help.adobe.com/fr_FR/FlashPl...
Manipulation de tableaux : http://help.adobe.com/fr_FR/FlashPl...
Ecouteurs d’événements : http://help.adobe.com/fr_FR/FlashPl...

Si vous souhaitez plus de précisions sur ces points, je vous encourage à parcourir le Wiki de Mediabox où vous trouverez de nombreux tutoriaux détaillés.

Le code

Voyons d’abord tout le code d’un coup.

const N:int = 5;                         // clous
const C:int = 5;                         // couleurs
const E:int = 10;                         // essais
const T:int = 30;                         // positions
var i:int;
var tours:int;
var ligne:Array;
var solution:Array = [0,0,0,0,0];
var couleurs:Array = [0,0,0,0,0];
var texte:TextField;
var bouton:Bouton = new Bouton();
var plateau:Plateau = new Plateau();
bouton.x = 208;
bouton.buttonMode = true;
bouton.addEventListener(MouseEvent.CLICK,valider);

addChild(bouton);
addChild(plateau);

// interface
var panneaux:Panneaux = new Panneaux();
panneaux.addEventListener(MouseEvent.MOUSE_DOWN, init);
panneaux.buttonMode = true;
addChild(panneaux);

// initialisation
function init(e:Event):void{
        while(numChildren>3) removeChildAt(3);
        tours = 0;
        for (i=0; i<N; i++) solution[i] = int(Math.random()*C)+1;
        creeLigne();
}

// créer une ligne
function creeLigne():void{
        ligne = [];
        for (i=0; i<N; i++) ajoutePion(i, 1);
        bouton.y = tours*T+T;
        ajouteTexte("Placez les clous et validez !");
}

// ajouter un pion
function ajoutePion(j:int, f:int):void{
        var p:Pions = new Pions();
        p.x = j*T+T;
        p.y = tours*T+T;
        p.couleur = 0;
        p.gotoAndStop(f);
        p.addEventListener(MouseEvent.CLICK,posePions);
        p.buttonMode = true;
        addChild(p);
        ligne.push(p);
}

// ajouter du texte
function ajouteTexte(t:String){
        texte =                 new TextField();
        texte.x =                 290;
        texte.y =                 tours*T+T;
        texte.width =                 300;
        texte.text =                 t;
        texte.textColor =         0xFFFFFF;
        addChild(texte);
}

// poser un pion
function posePions(e:MouseEvent){
        var p:Object = e.currentTarget;
        if (p.couleur < C) p.couleur++ else p.couleur = 0;
        p.gotoAndStop(p.couleur+1);
}

// valider
function valider(e:MouseEvent){
        var p:int = 0;
        var c:int = 0;
        var sol:Array = [0,0,0,0,0];
        var cou:Array = [0,0,0,0,0];
        var t:Object;
        for (i=0; i<N; i++)        {
                t = ligne[i];
                if (t.couleur == solution[i])        {
                        p++;
                }else{
                        sol[solution[i]-1]++;
                        cou[t.couleur-1]++;
                }
                t.removeEventListener(MouseEvent.CLICK,posePions);
                t.buttonMode = false;
        }
        for (i=0; i<C; i++) c +=  Math.min(sol[i],cou[i]);
        texte.text = "Emplacements : " + p + " - Couleurs : " + c;
        texte.textColor = 0xFFFFFF;
        tours++;
        if (p == N){
                finPartie(true);
        }else{
                if (tours == E) finPartie(false) else creeLigne();
        }
}

// fin de partie
function finPartie(w:Boolean){
        bouton.y = tours*T+T;
        if (w) {
                ajouteTexte("Gagné !");
                panneaux.gotoAndStop(3);
        } else {
                ajouteTexte("Perdu !");
                panneaux.gotoAndStop(2);
        }
        for (i=0; i<N; i++) ajoutePion(i, solution[i]+1);
        addChild(panneaux);
}

Etude du programme

Comme d’habitude j’utilise la bibliothèque de Flash pour gérer les graphismes, si vous n’utilisez pas Flash voici les modifications à faire dans le programme :

Panneaux : - un clip qui regroupe tous les panneaux d’interface - un panneau différent par frame

Pions : - un clip qui regroupe toutes les couleurs des pions - une couleur sur chaque frame - le première frames représente un emplacement

Bouton : - le bouton de validation

Plateau : - le plateau de jeu (décor)

Allez c’est parti pour l’étude pas à pas :

const N:int = 5;                         // clous
const C:int = 5;                         // couleurs
const E:int = 10;                         // essais
const T:int = 30;                         // positions
var i:int;
var tours:int;
var ligne:Array;
var solution:Array = [0,0,0,0,0];
var couleurs:Array = [0,0,0,0,0];
var texte:TextField;
var bouton:Bouton = new Bouton();
var plateau:Plateau = new Plateau();
bouton.x = 208;
bouton.buttonMode = true;
bouton.addEventListener(MouseEvent.CLICK,valider);

addChild(bouton);
addChild(plateau);

On se débarrasse tout de suite des déclarations globales, tout ceci est connu si vous avez fait les exercices précédents, nous avons les constantes (invariables), les pointeurs pour les boucles, les tableaux et les objets.

// interface
var panneaux:Panneaux = new Panneaux();
panneaux.addEventListener(MouseEvent.MOUSE_DOWN, init);
panneaux.buttonMode = true;
addChild(panneaux);

J’ajoute le panneau d’accueil du jeu, puis je lance l’initialisation.

// initialisation
function init(e:Event):void{
        while(numChildren>3) removeChildAt(3);
        tours = 0;
        for (i=0; i<N; i++) solution[i] = int(Math.random()*C)+1;
        creeLigne();
}

Je supprime tous les objets à part les trois premiers de la liste d’affichage, je remet le nombre de tours à zéro, je tire une solution aléatoire, et je crée la première ligne de pions.

// créer une ligne
function creeLigne():void{
        ligne = [];
        for (i=0; i<N; i++) ajoutePion(i, 1);
        bouton.y = tours*T+T;
        ajouteTexte("Placez les clous et validez !");
}

A chaque fois que je veux créer une nouvelle ligne, je vide le tableau de la ligne en cours, j’ajoute 5 pions (on vois ça juste après), je place le bouton de validation en face de la ligne nouvellement créée, et j’ajoute le texte de départ (on vois ça aussi après).

// ajouter un pion
function ajoutePion(j:int, f:int):void{
        var p:Pions = new Pions();
        p.x = j*T+T;
        p.y = tours*T+T;
        p.couleur = 0;
        p.gotoAndStop(f);
        p.addEventListener(MouseEvent.CLICK,posePions);
        p.buttonMode = true;
        addChild(p);
        ligne.push(p);
}

A chaque fois que j’ajoute un nouveau pion, je le crée, je le positionne en X et en Y en fonction de la place dans la ligne et de la position de la ligne sur Y, je lui donne une couleur par défaut de zéro, donc vide, je lui donne un écouteur qui va permettre de changer sa couleur, je l’ajoute à l’affichage et je l’enregistre dans la ligne nouvellement créée.

// ajouter du texte
function ajouteTexte(t:String){
        texte =                 new TextField();
        texte.x =                 290;
        texte.y =                 tours*T+T;
        texte.width =                 300;
        texte.text =                 t;
        texte.textColor =         0xFFFFFF;
        addChild(texte);
}

Lorsque je veux ajouter du texte, je crée un nouveau textField, je le place sur X (toujours à la même position), je le place sur Y en fonction de la position de la ligne qu’on est en train de jouer, je lui donne sa taille, sa couleur et son contenu (à vous d’adapter au besoin), puis je l’ajoute à la liste d’affichage.

// poser un pion
function posePions(e:MouseEvent){
        var p:Object = e.currentTarget;
        if (p.couleur < C) p.couleur++ else p.couleur = 0;
        p.gotoAndStop(p.couleur+1);
}

Lorsque le joueur veut changer la couleur d’un pion, donc lorsqu’il cliques dessus, j’avance d’une frame dans le clip du pion, ce qui change sa couleur. Si je dépasse le nombre de couleurs autorisé je reviens au début du clip, donc à un emplacement vide. Notez que la couleur est enregistrée dans un paramètre du clip nommé "couleur", cela servira plus tard pour éviter d’aller regarder le numéro de la frame affichée par le clip.

// valider
function valider(e:MouseEvent){
        var p:int = 0;
        var c:int = 0;
        var sol:Array = [0,0,0,0,0];
        var cou:Array = [0,0,0,0,0];
        var t:Object;
        for (i=0; i<N; i++)        {
                t = ligne[i];
                if (t.couleur == solution[i])        {
                        p++;
                }else{
                        sol[solution[i]-1]++;
                        cou[t.couleur-1]++;
                }
                t.removeEventListener(MouseEvent.CLICK,posePions);
                t.buttonMode = false;
        }
        for (i=0; i<C; i++) c +=  Math.min(sol[i],cou[i]);
        texte.text = "Emplacements : " + p + " - Couleurs : " + c;
        texte.textColor = 0xFFFFFF;
        tours++;
        if (p == N){
                finPartie(true);
        }else{
                if (tours == E) finPartie(false) else creeLigne();
        }
}

On attaque la plus grosse partie, lorsque le joueur clique sur le bouton de validation, le programme vérifie les combinaisons, pour cela je boucle sur les pions contenus dans la ligne en cours, pour chacun je regarde si la couleur correspond à la couleur enregistrée dans la solution, pour chaque couleur valide et bien placée j’incrémente un petit compteur. Si le pion testé ne correspond pas à la solution, je dois savoir si il est de la bonne couleur mais mal placé, pour cela j’enregistre sa position et sa couleur dans deux petits tableaux temporaires (il y a un décalage de 1 car mon décompte de couleurs commence à 1 et mes tableaux à 0). Je retire l’écouteur sur le pion afin que le joueur ne puisse plus le modifier.

Je fais ensuite une boucle sur le nombre total de couleurs du jeu, ce pointeur va me donner le nombre de pions mal placés mais de la bonne couleur, pour cela je regarde la valeur minimum entre le placement et la couleur, un pion mal placé aura un placement de 0 et si il a la bonne couleur il aura une couleur de 1.

J’affiche ensuite le texte d’information donnant le nombre de pions bien placés ayant la bonne couleur, et le nombre de pions mal placés mais avec la bonne couleur.

Enfin, j’incrémente le nombre de tours, puis je vérifie si le joueur à gagné (tous les pions de la bonne couleur bien placés), si ce n’est pas le cas, si le joueur à atteint le nombre de tours maximum il perd la partie, sinon je crée une nouvelle ligne pour le laisser continuer à chercher.

// fin de partie
function finPartie(w:Boolean){
        bouton.y = tours*T+T;
        if (w) {
                ajouteTexte("Gagné !");
                panneaux.gotoAndStop(3);
        } else {
                ajouteTexte("Perdu !");
                panneaux.gotoAndStop(2);
        }
        for (i=0; i<N; i++)        ajoutePion(i, solution[i]+1);
        addChild(panneaux);
}

A la fin de la partie, je place le bouton de validation sur la dernière ligne, selon si le joueur à gagné ou perdu j’affiche le texte et le panneau correspondant, puis j’affiche la solution sur la dernière ligne, et pour terminer j’affiche le panneau de fin.

Conclusion

Mastermind est un jeu très simple que vous pouvez décliner sous de nombreuses formes, je n’ai pas grand chose à dire en conclusion, techniquement le jeu n’est pas dur, je joue cependant avec les textes afin de vous entraîner à les manipuler comme tous les autres objets.

Sources

Version Flash CS5.5

Zip - 153.8 ko