Admin/Dev

10
Févr.
2019

Développer un MasterMind en Javascript - Partie 5

Publié par sky

Voici le dernier épisode de notre série consacrée à la réalisation d'un MasterMind en Javascript. A la fin de l'épisode 4, nous avions un jeu complet et utilisable. Alors que peut on rajouter de plus ?

Le but de cet article est de finaliser notre jeu avec les fonctionnalités que l'on souhaiterait y voir. C'est tout l'avantage de développer sa propre version d'un jeu ou d'un logiciel, c'est que l'on peut lâcher son imagination et intégrer exactement ce que l'on souhaite, sous réserve qu'on soit capable de le créer.

En premier, ce que je souhaiterai voir dans cette version de notre Mastermind, c'est un bouton pour relancer la partie. Cela éviterait de recharger la page, et améliorerait donc l'ergonomie pour le joueur.

Mais quitte à ajouter une bouton, pourquoi pas ne pas en ajouter un second, voir plusieurs afin de lancer des parties dans des difficultés différentes ?

Ha ! Et comme je veux pouvoir faire jouer ma fille, qui n'a que 5 ans, pourquoi pas imaginer un mode facile ou il n'y aurait pas de doubles, voir, mais plus complexe, ou l'affichage des résultats à droite indique la position des éléments bien placés et mal placés.

Voila qui nous donnera déjà pas mal de travail. Quant à moi, j'aimerai une difficulté supérieure, donc, je vais ajouter des couleurs et des colonnes à deviner.

Compilons tout cela pour définir les tâches à réaliser.

Dans l'ordre, nous allons :

  • définir les niveaux de difficulté, en ajoutant les variables autorisant les doubles et définissant le mode de résolution du jeu
  • ajouter des couleurs à notre liste de couleur
  • modifier la méthode qui permet de lancer la partie selon le mode de difficulté choisi
  • ajouter les boutons à notre interface pour lancer les parties
  • modifier la méthode qui défini la solution en début de partie pour gérer les doubles ou les non-doubles
  • modifier la méthode qui vérifie si le joueur à gagner selon le mode d'affichage des résultats

Il n'y a plus qu'à.


1/ Définir les niveaux de difficulté.

Pour faire les choses biens, mais sans trop en faire, je choisi de mettre 4 niveaux de difficulté. Cela permettra au joueur d'avoir un peu de choix, sans trop lui en donner, et le perdre complètement. Encore une fois, le choix est un mélange entre ce que l'on souhaite et une ergonomie optimale.

Nos niveaux de difficulté n'ont besoin d'être définis qu'une fois, au chargement de l'objet, nous allons donc les ajouter parmi les autres variables, définies au début de l'objet. Aussi, afin de simplifier la gestion de ces niveaux de difficulté, nous allons tous les insérer dans la même liste.

Ainsi dans votre fichier, ajouter parmi les variables déjà écrites

difficulties: {
easy: {
lines: 12, columns: 4, colors: 5, double: false, locCheck: true,
}, normal: {
lines: 12, columns: 4, colors: 6, double: true, locCheck: true,
}, hard: {
lines: 12, columns: 5, colors: 8, double: true, locCheck: false,
}, extreme: {
lines: 12, columns: 6, colors: 8, double: true, locCheck: false,
},
},

Comme vous pouvez le voir, chacun des niveaux indique le nombre de lignes, de colonnes, et de couleurs, comme peut le faire la valeur settings. A cela nous avons ajouté les deux variables correspondantes aux améliorations souhaitées, la gestion des doubles, et la méthode de résolution.

Ce n'est pas plus compliqué que cela.

 

2/ Ajouter des couleurs à notre liste de couleur

Il s'agit de l'étape la plus simple, nous avons désormais besoin de 8 couleurs, ajoutons donc 2 couleurs pour compléter la liste.

7: '#00d8d5', // cyan
8: '#8a05fa', // violet

Et hop, c'est faiiiit !

 

3/ Modifier la méthode qui permet de lancer la partie

Actuellement, notre fonction startGame ressemble à

startGame: function() {
this.drawGameBoard(); this.resetGame(); this.defineSoluce();
},

Nous allons lui ajouter un paramètre qui sera notre niveau de difficulté, et faire en sorte que la variable settings se mette à jour selon le niveau indiqué.

Ré-écrivons la.

startGame: function(difficulty) {
this.settings = this.difficulties[difficulty]; this.drawGameBoard(); this.resetGame(); this.defineSoluce();
},

Comme nous avons ajouté un paramètre à notre fonction, qui est appelée dans notre fonction initialise, nous devons modifier cette dernière en conséquence.

Afin que le jeu se lance automatiquement en mode facile, j'ajoute le paramètre tel que

initialise: function() {
this.startGame('easy');
},

Passons à l'étape suivante.

 

4/ Ajouter les boutons à notre interface pour lancer les parties

Pour cela nous devons modifier notre fichier HTML, nous pourrions le faire directement depuis l'objet Javascript, mais par simplicité, il est préférable d'écrire dans le HTML.

Ouvrons donc le HTML, et insérons nos boutons, dans le div que nous avions créé, et laissé vide dont la classe CSS est menus.

<div class="menu" onclick="MasterMind.startGame('easy');">
Jouer "Facile"
</div>
<div class="menu" onclick="MasterMind.startGame('normal');">
Jouer "Normal"
</div>
<div class="menu" onclick="MasterMind.startGame('hard');">
Jouer "Difficile"
</div>
<div class="menu" onclick="MasterMind.startGame('extreme');">
Jouer "Extr&ecirc;me"
</div>

Et pour rendre ces bouton un peu plus joli, nous allons leur appliquer une classe de style CSS. Insérer le code suivant dans votre fichier MasterMind.css

.menus .menu {padding:10px 25px; margin:8px; background:#4366b4; color:white; cursor:pointer; width:200px;}

 

5/ Modifier la méthode qui défini la solution

Lors de la définition de la solution, nous devons désormais prendre en compte le réglage des doubles.

Notre fonction qui était

defineSoluce: function() {
this.game['soluce'] = new Array(); for (i = 1; i <= this.settings['columns']; i++) {
color = parseInt(Math.random()*this.settings['colors'])+1; this.game['soluce'][i] = color;
}
},

devient

defineSoluce: function() {
this.game['soluce'] = new Array(); for (i = 1; i <= this.settings['columns']; i++) {
color = parseInt(Math.random()*this.settings['colors'])+1; while (this.settings['double'] == false && this.game['soluce'].indexOf(color) != -1) {
color = parseInt(Math.random()*this.settings['colors'])+1;
} this.game['soluce'][i] = color;
}
},

Cela nécessite peut être un peu d'explication ? Comme précédemment, nous définissons une couleur, mais avant de la mettre dans la liste, nous vérifions que la couleur ne fasse pas partie des couleurs déjà sélectionnées. Si c'est le cas, nous définissons une nouvelle couleur et relançons le test. Tout cela, en boucle, jusqu'à obtenir une couleur qui convienne.

Attention, si vous définissez un nombre de colonnes inférieur au nombre de couleurs disponibles, votre boucle tournera indéfiniment puisqu'il arrivera un moment ou toutes les couleurs auront été choisies et la fonction continuera d'en chercher une nouvelle, sans espoir. Heureusement Javascript est capable de le détecter, et coupera la boucle à un moment, mais ce n'est pas le cas de tous les languages de programmation, c'est à vous de vérifier que vos boucles disposent toujours d'une sortie.

Passons à la dernière tâche, et non des moindres.

6/ Modifier la méthode qui vérifie si le joueur à gagner

Evidemment, j'ai gardé la modification la plus complexe pour la fin. Car, oui, cette modification nécessite un peu de réflexion. Mais nous allons voir tout cela ensemble.

Si vous avez bien suivi, nous devons modifier la fonction checkLine qui n'est déjà pas si simple.

A l'intérieur de la fonction, avant de tester la ligne, et d'afficher les résultats, nous devons ajouter une condition qui vérifie le mode de vérification. Puis faire une dérivation toute simple avec un simple if, puis écrire l'autre méthode de vérification. Nous pouvons évidemment conserver la méthode actuelle, telle qu'elle est, puisqu'elle fonctionne parfaitement.

L'alternative, même si elle a l'air plus simple nécessite un peu plus de réflexion. Vous allez le voir, le début est similaire, puisque la recherche des couleurs bien placées est identique. Cette partie, commune aux deux solutions, aurait d'ailleurs pu être sortie de la condition. C'est la recherche des pions mal placés, qui est finalement plus complexe.

Voici la version modifiée

checkLine: function(line) {
/* Verifie si la ligne est bien la ligne courante, verifie en meme temps, si la partie est toujours active */ if (line != this.game['turn']) {
return;
} /* Verifie que la ligne a ete entierement remplie par le joueur */ for (i = 1; i <= this.settings['columns']; i++) {
if (!this.game['selection'][i]) {
return;
}
} /* Duplique la solution pour pouvoir la modifier sans alterer l originale */ soluce = this.game['soluce'].slice(0); /* Verifie le mode de verification */ if (this.settings['locCheck'] === false) {
/* Initialise les variables de verification */ correct = 0; misplaced = 0; /* Verifie les pions bien places */ for (i = 1; i <= this.settings['columns']; i++) {
if (this.game['selection'][i] == soluce[i]) {
correct++; soluce[i] = 0; this.game['selection'][i] = 0;
}
} /* Verifie si tous les pions sont biens places, et auquel cas, afficher la victoire */ if (correct == this.settings['columns']) {
/* Utilise un return pour sortir de la methode et ne pas continuer la verification */ return this.displayWin();
} /* Verifie les pions mal places, parmi les pions restant */ for (i = 1; i <= this.settings['columns']; i++) {
if (this.game['selection'][i] == 0) {
continue;
} loc = soluce.indexOf(this.game['selection'][i]); if (loc != -1) {
this.game['selection'][i] = 0; soluce[loc] = 0; misplaced++;
}
} /* Affiche le bon nombre de pions bien places */ for (i = 1; i <= correct; i++) {
pion = document.createElement('div'); pion.className = 'correct';                 document.getElementById('result-'+this.game['turn']+'-'+i).appendChild(pion);
} /* Affiche le bon nombre de pions mal places */ for (j = i; j < i+misplaced; j++) {
pion = document.createElement('div'); pion.className = 'misplaced';                 document.getElementById('result-'+this.game['turn']+'-'+j).appendChild(pion);
}
} else {
correct = 0; /* Verifie les pions bien places */ for (i = 1; i <= this.settings['columns']; i++) {
if (this.game['selection'][i] == this.game['soluce'][i]) {
correct++; this.game['selection'][i] = 0; soluce[i] = 0; /* Indique le pion bien place */ pion = document.createElement('div'); pion.className = 'correct'; document.getElementById('result-'+this.game['turn']+'-'+i).appendChild(pion);
}
} /* Verifie si tous les pions sont biens places, et auquel cas, afficher la victoire */ if (correct == this.settings['columns'])
return this.displayWin();
/* Verifie les pions mal places, parmi les pions restant */ for (i = 1; i <= this.settings['columns']; i++) {
if (this.game['selection'][i] == 0)
continue;
loc = soluce.indexOf(this.game['selection'][i]); if (loc != -1) {
this.game['selection'][i] = 0; soluce[loc] = 0; /* Indique le pion mal place */ pion = document.createElement('div'); pion.className = 'misplaced'; document.getElementById('result-'+this.game['turn']+'-'+i).appendChild(pion);
}
}
} /* Prepare le jeu pour le tour suivant */ /* Re-initialise la selection du joueur */ this.game['selection'] = new Array(); /* Retire le marquage visuel de la ligne courante  */ document.getElementById('turn-'+this.game['turn']).className = ''; /* Verifie que la ligne n etait pas la derniere, si auquel cas, afficher la defaite */ if (this.game['turn'] == this.settings['lines']) {
/* Utilise un return pour sortir de la methode et ne pas continuer la verification */ return this.displayLose();
} /* Deplace le curseur sur la ligne suivante */ this.game['turn'] ++; /* Applique le marquage sur la nouvelle ligne courante */ document.getElementById('turn-'+this.game['turn']).className = 'selected'; /* Place le curseur sur la premiere case */ this.game['column'] = 1; /* Applique le marquage sur la premiere case */ document.getElementById('turn-'+this.game['turn']+'-1').className = 'selected';
},

Et voilà, avec toutes ces modifications, notre objectif est obtenu. Evidemment, il y aurait encore des milliers de choses que l'on pourrait inventer pour rendre le jeu encore plus attractif. Vous pouvez vous définir d'autres objectifs, et prévoir une mise à jour supplémentaire. N'hésitez pas à partager vos idées, et bien sur la manière dont vous l'avez réalisé.

Je vous mets une nouvelle fois les sources telles qu'elles sont à la fin de cet article.

 
Sommaire de la série
 
 
Commentaires
Commentaire de manu le 10 Mars 2022 à 17:11

Beau boulot! tout est expliqué en détail! En tant que débutant je trouve ça génial. Un grand merci pour ton travail! Je vais le potasser!

 
Commentaire de sky le 10 Mars 2022 à 17:15

Salut Manu, merci ! Bon courage, j'espère que tout sera assez clair. Sinon n'hésite pas à demander dans les commentaires ;-)

 

 

Poster un commentaire
En postant sur skymac.org, je m'engage à être courtois et à ce que mon message soit pertinent avec le sujet de l'article.
En outre, j'accepte, sans condition, que mon message soit refusé et supprimé si ces règles ne sont pas appliquées.
Ouvrir le panneau de gestion des cookies
Fermer le panneau
Ce site utilise des cookies pour assurer son bon fonctionnement. Il utilise aussi des cookies issues de services tiers permettant de proposer des fonctionnalités avancées. À tout moment, vous pouvez choisir quels services vous souhaitez activer ou refuser, afin de retirer votre consentement quant à l'utilisation des cookies.
 
Personnalisation des services
Vous êtes libre de choisir quels services vous souhaitez activer. En autorisant ces services tiers, vous acceptez le dépôt et la lecture de cookies et l'utilisation de technologies de suivi nécessaires à leur bon fonctionnement. En retirant votre consentement pour certains de ces services, certaines fonctionnalités du site peuvent ne plus fonctionner.
Navigation du site  En savoir plus
Le site écrit un cookie de session permettant son bon fonctionnement et aidant à la navigation. Il ne peut être désactivé.
Utilisation : 1 cookie, enregistre l'identifiant de la session.
Durée de vie : Le cookie est présent pendant toute la session sur le site. Il devient obsolète après 24 minutes d'inactivité.
Obligatoire
Popup Média
Afficher des vidéos depuis Yoube ou Dailymotion.
 
Tout accepter Tout refuser Gérer