Jeu de morpions JavaScript
Les règles simplissimes du morpions en font un exercice
idéal de programmation. C'est en JavaScript que nous
nous proposons de développer l'algorithme de ce jeu.
Mais si JavaScript n'est pas votre tasse de... café,
sachez que vous pouvez quand même, à
l'aide d'une seule balise HTML, afficher ce jeu dans
vos pages perso.
Mise en place du jeu en HTML
Le jeu doit s'afficher dans une page HTML. Nous utilisons
un tableau de 12 cellules pour y insérer des champs
de formulaire. Un simple éditeur de texte nous suffira
pour le saisir. Le formulaire est baptisé "toto".
<HTML><HEAD></HEAD><BODY>
<FORM NAME="toto">
<TABLE BORDER=0>
<TR>
<TD ALIGN=center>
<INPUT TYPE=text VALUE="X"
SIZE=1 MAXLENGTH=1 >
</TD>
<TD ALIGN=center>
<INPUT TYPE=text VALUE="X"
SIZE=1 MAXLENGTH=1 >
</TD>
<TD ALIGN=center>
<INPUT TYPE=text VALUE="X"
SIZE=1 MAXLENGTH=1 >
</TD>
</TR>
<TR>
<TD ALIGN=center>
<INPUT TYPE=text VALUE="X"
SIZE=1 MAXLENGTH=1 >
</TD>
<TD ALIGN=center>
<INPUT TYPE=text VALUE="X"
SIZE=1 MAXLENGTH=1 >
</TD>
<TD ALIGN=center>
<INPUT TYPE=text VALUE="X"
SIZE=1 MAXLENGTH=1 >
</TD>
</TR>
<TR >
<TD ALIGN=center>
<INPUT TYPE=text VALUE="X"
SIZE=1 MAXLENGTH=1 >
</TD>
<TD ALIGN=center>
<INPUT TYPE=text VALUE="X"
SIZE=1 MAXLENGTH=1 >
</TD>
<TD ALIGN=center>
<INPUT TYPE=text VALUE="X"
SIZE=1 MAXLENGTH=1 >
</TD>
</TR>
<TR>
<TD ALIGN=center COLSPAN=3>
<INPUT
TYPE=button VALUE="Effacer" >
</TD>
</TR>
</TABLE>
</FORM></BODY></HTML>
Le code JavaScript
La code HTML étant en place, nous devons maintenant
inclure dans la balise HEAD, les fonctions qui vont animer
notre jeu. Commençons par le contrôle le plus
simple : un bouton effaçant le contenu de chacune
des 9 cellules afin de permettre de rejouer. Nous allons
déclarer une fonction qui aura ce rôle et faire
en sorte qu'un clic sur le bouton lance son exécution.
On insère une balise <SCRIPT> dans l'en-tête
du document :
<HEAD><SCRIPT LANGUAGE="JavaScript">
function efface(){
for (n=0; n<=8; n++) {document.toto.elements[n].value="''
}
}
</SCRIPT></HEAD>
Notre fonction boucle 9 fois en incrémentant une
variable n. Chaque fois un élément du formulaire
"toto", élément désigné par
sa position dans le tableau "elements" est vidé de
tout contenu. Rappelons que c'est le navigateur qui tient
comptabilité des formulaires et des éléments
de formulaire présents dans le document, en deux
tableaux nommés "forms" et "elements". L'expression
"document.forms[0]elements[0] est du
pur JavaScript et désigne toujours le premier objet
(l'index commence à zéro) du premier formulaire
d'un document.
Dès lors notre bouton de formulaire peut jouer son
rôle . Il suffit d'inclure dans la balise qui
le définit, un gestionnaire d'événement
"onClick" qui appelle l'exécution de la fonction
créée :
<INPUT TYPE=button VALUE="Effacer" onClick="efface()">
Ça marche ! Occupons nous maintenant de rendre ce
jeu convivial. Un clic doit suffire à l'utilisateur
pour placer sa croix dans une cellule. Les champs de formulaire
ignorant l'événement onClick nous interceptons
un autre type d'action : l'activation du champs par
positionnement du curseur d'insertion. Cet événement
est nommé "Focus". Pour le reste, le procédé
d'écriture du script est le même : On
déclare un fonction ("jeuPerso") exécutant
l'action souhaitée, dans la balise script d'en-tête
du document. Puis on veille à appeler cette fonction
en modifiant le code HTML. La fonction d'abord : elle doit
connaître la cellule cliquée afin d'en changer
la valeur si elle est vide. Nous lui passerons donc cette
information en paramètre (CaseDuClic) lors de son
appel.
function jeuPerso(CaseDuClic){
if ( CaseDuClic.value=="" ) { CaseDuClic.value='X'
}
}
Pour appeler la fonction nous modifions comme suit le code
HTML définissant chaque champs de texte du
formulaire.
<INPUT TYPE=text VALUE="X" SIZE=1 MAXLENGTH=1
onFocus="jeuPerso(this)">
"This" désigne l'objet en cours, ici l'élément
de formulaire cliqué. C'est lui qu'il nous faut passer
à la fonction (dans la variable CaseDuClic).
Il ne nous manque plus qu'une chose mais de taille :
un fonction permettant à la machine de jouer à
son tour. Une telle fonction, nous pourrions la nommer "jeuAuto()"
et faire en sorte qu'elle soit appelée immédiatement
après jeuPerso(). C'est là un chose aisée :
function jeuPerso(CaseDuClic){
if ( CaseDuClic.value=="" ) { CaseDuClic.value='X'
}
jeuAuto()
}
Plus difficile est de concevoir cette fonction jeuAuto().
Il s'agit ni plus ni moins que d'apprendre à une
machine les règles du morpions ! Une analyse
préalable s'impose.
L'algorithme du jeu
Avant d'écrire la fonction jeuAuto() nous devons
nous demander comment NOUS jouons nous au morpions lorsque
c'est notre tour de jouer.
Nous cherchons tout d'abord un coup gagnant qui mettrait
fin à la partie. Avec la même urgence aussi,
nous cherchons à parer un coup prochain de l'adversaire
qui serait un coup gagnant pour lui. Notez que dans ces
deux cas, nous cherchons sur tous les axes du jeu, deux
cases remplies identiquement, accompagnées d'une
troisième case vide. Dans tous les cas, que ces
deux cases soient remplies par nous ou par notre adversaire,
nous DEVONS occuper la troisième !
Ensuite, si notre première recherche n'aboutit
pas, nous cherchons à effectuer un coup pertinent.
Parler de tactique ici semble un peu forcé. Nous
ne saurions donner qu'une seule case dont l'occupation
est tactique : le centre du plateau. Cette position
est la seule qui entre dans la constitution de 4 lignes.
Enfin, si le centre est déjà occupé,
il y-a -t'il autre chose à faire que de se placer
sur la première case vide ? Certes un algorithme
plus pertinent jouerais ce coup en fonction de celui de
l'adversaire. Notre jeu deviendrait ainsi imbattable.
Mais paradoxalement il ne nous semble pas inintéressant
que notre jeu ait cette faiblesse. Qui voudrait jouer
avec l'assurance qu'il va perdre ?
Notre fonctions jeuAuto() devra donc procéder de
même. Chercher d'abord à occuper une place
nécessaire (coup gagnant ou coup défensif
obligé), chercher en un deuxième temps s'il
est possible d'occuper la case centrale, enfin en désespoir
de cause, occuper la première cellule vide. Notre
fonction peut être décomposée en trois
sous fonctions que nous nommerons : UnCoupObligé(),
LaCaseCentrale(), UneCaseVide(). Nous ferons en sorte que
ces fonctions renvoient une valeur "true" ou "false" afin
de pouvoir écrire :
function jeuAuto() {
if ( UnCoupObligé()
) { }
else {
if ( LaCaseCentrale()
) { }
else {
UneCaseVide();
return null
}
}
}
De la sorte, les deuxième fonction ne sera exécutée
que si celle qui précède renvoie une valeur
fausse. De même la dernière fonction de s'exécutera
qu'à la condition que le centre est déjà
occupé (la fonction LaCaseCentrale() renvoyant dans
ce cas la valeur fausse).
Nous commençons par rédiger la fonction UneCaseVide().
Cette fonction appelée en dernier recours doit identifier
la première case vide et y placer un "O" correspondant
au coup joué par la machine. Toujours dans la balise
<SCRIPT...> on insère maintenant les lignes
suivantes :
function UneCaseVide() {
for (n=0; n<=8; n++) {
// pour chacune des 9 cases, on teste si elle est vide
if (document.toto.elements[n].value=="") {
document.toto.elements[n].value="O";
//
auquel cas on s'y place
return null
//
et on sort de la boucle
}
}
}
La fonction qui vérifie si la case centrale est
disponible et renvoie "false dans le cas contraire ne pose
pas d'avantage de problème :
function LaCaseCentrale() {
if ( document.toto.elements[4].value=="" ) { /* Le
cinquième objet du formulaire, indexé 4, est
la cellule centrale */
document.toto.elements[4].value="O";
return true
}
else { return false} //
en retournant "false, on donne lieu
} //
à l'exécution de la fonction UneCaseVide()
"Last but not least" la fonction UnCoupObligé()
va nous contraindre à saisir beaucoup plus de code.
Cette fonction doit très précisément
prendre chacun des axes du jeu pour vérifier si s'y
trouve deux valeurs identique (croix ou cercle) et une case
vide. Dans cette configuration en effet, il FAUT jouer dans
la case vide. La fonction renverra "false" si un tel état
de fait n'est pas trouvé afin de laisser s'exécuter
les fonctions suivantes LaCaseCentrale()
ou UneCaseVide().
Il faut tester chaque axe du jeu c'est-à-dire qu'il
faut observer plusieurs séries de cellules. Notre
premier travail va consister à établir la
liste de ces axes ou séries de cases. Nous nommerons
ces 8 lignes ou séries possibles axeN.
function UnCoupObligé()
{
/*
Création de 9 tableaux (axe1, axe2, axe3,...) de
4 entrées chacun (la première entrée
étant indexée 0, nous ne l'utiliserons pas
par commodité. */
for (n=1; n<=8; n++) { eval ("axe" + n +" = new Array(4)")
}
// population
des tableaux axe1, axe2, axe3, séries horizontales
for (n=1; n<=3; n++) {
axe1[n] = document.toto.elements[n-1]
/*
rappel: l'élément Array est indexé
depuis 0 */
}
for (n=4; n<=6; n++) {
axe2[n-3] = document.toto.elements[n-1]
}
for (n=7; n<=9; n++) {
axe3[n-6] = document.toto.elements[n-1]
}
// population
des tableaux axe4, axe5, axe6, séries verticales
for (n=1; n<=3; n++) {
for (x=1 ; x<=3 ; x++) {
eval ( "axe" + (x+3) + "[n] =
axe" + n + "[x]")
/* par exemple : axe"3+3" [2] = axe2 [3]
* /
}
}
// première diagonale 7ème
axe
for (n=1; n<=3 ; n++) {
eval ( "axe7[n]=axe" +n+ "[n]" )
}
// population du huitième axe
(du haut droit vers le bas gauche)
axe8[1] = axe1[3];
axe8[2] = axe2[2];
axe8[3] = axe3[1];
On dispose dès lors de 8 tableaux ou listes reprenant
les axes ou lignes du jeu. Il nous est possible de tester
une à une ces lignes afin de déterminer si
l'une d'entre elles contient 2 valeurs identiques non vides
et une troisième vide. Nous pourront ainsi agir en
conséquence. L'algorithme est simple et pourtant
efficace. Il pourra resservir à d'autre langage de
programmation... ;-)
// algorithme de test
for (n=1; n<=8; n++) {
y = eval ("axe" + n) /*
à chaque entrée dans la boucle "y" représente
une ligne à tester */
if ( y[1].value == y[2].value
&& y[1].value != "" && y[3].value
== "" ){
y[3].value = "O";
return true
}
if ( y[1].value == y[3].value
&& y[1].value != "" && y[2].value
== "" ){
y[2].value="O";
return true
}
if ( y[3].value == y[2].value &&
y[2].value != "" && y[1].value ==
"" ){
y[1].value="O";
return true
}
} /*
enfin si ce test des axes ne donne aucun resultat, la fonction
retourne false */
return false
}
Notre jeu est prêt. Les fanatiques du morpions auront
tôt fait de déceler les faiblesses de l'algorithme
et de trouver les séquences gagnantes. B2,C1,C3 ou
C1, A3, C3. Nous pourrions aménager aisément
la fonction UneCaseVide() afin d'éviter à
l'ordinateur une défaite mais il nous semble que
notre jeu est plus intéressant ainsi.
Utiliser le jeu sur votre site
Yazo.net vous offre d'afficher un jeu de morpions sur votre
page perso sans écrire une ligne de JavaScript. Insérer
seulement dans votre code HTML, à l'emplacement souhaité,
la ligne suivante :
<SCRIPT LANGUAGE="JavaScript"
SRC="http://www.yazo.net/techniquest/morpions.js"></SCRIPT>
Prévoir 60 x 160 pixels minimun. Connexion rapide
préferable ! Un exemple dans un cadre bleu :
<table bgcolor="blue">
<tr><td>
<script language="JavaScript" src="http://www.yazo.net/techniquest/morpions.js></script>
</td></tr></table>
Affichera sur votre homepage :
Dans un article ancien, le CrapoWeb présentait les
notions nécessaires pour débuter
en JavaScript.
Netscape propose le téléchargement du manuel
de référence de JavaScript sur son site :
http://developper.netscape.com/docs/manuals/
communicator/jsguide4/index.htm
|