Copyright ©
présent sur cette page.
Tous les TP se font en environnement Unix
.
Vous devez redémarrer votre machine de la salle de TP sous Unix
si elle est sous Windows
.
Pour faire ce TP, vous devez reprendre des choses faites lors du TP précédent :
index.html
qui affiche la grille initiale du jeu avec le score, le bouton Nouvelle partie
et les 4 boutons de direction,styles2048.css
qui contient les styles de la page du jeu,affiche-logs.php
qui affiche les logs stockés dans logs-2048.txt
, jeu-2048.php
qui inclut fonctions-2048.php
et appelle la fonction write_log($mesg)
pour ajouter un log lors de chaque clic sur un des 5 boutons,fonctions-2048.php
qui contient à minima la fonction write_log($mesg)
. Pour travailler depuis chez vous sur le serveur web que vous utilisez en TP, suivez les instructions sur cette page.
La plupart des questions posées dans les sujets de TP sont suivies d'une Aide / Mémo.
Avant de faire une question, il faut lire l'Aide / Mémo qui suit la question.
L'objectif de ce TP est d'implémenter côté serveur l'algorithme du jeu 2048 en PHP pour faire fonctionner le jeu sur la page que vous avez conçue lors du TP précédent :
Nouvelle partie
jeu-2048.php
, fonctions-2048.php
et styles2048.css
du TP précédent.
Avant de programmer le jeu, il est nécessaire que la grille s'affiche après un clic sur l'un des boutons du jeu.
Pour l'instant, la page du jeu est jeu-2048/index.html
. Lorsque vous cliquez sur un bouton, l'URL
devient :
https://lifrw.univ-lyon1.fr/prenom.nom/jeu-2048/jeu-2048.php?action-joueur=xxx
où xxx
est remplacé par la valeur du bouton cliqué.
Pour commencer, il faut que chaque action de jeu affiche la grille comme le fait actuellement jeu-2048/index.html
. Pour cela, il faut transférer dans jeu-2048/jeu-2048.php
le code HTML
de jeu-2048/index.html
.
Comme vous l'avez vu au TP précédent, rappelez-vous qu'une page de type .php
peut contenir du code HTML
comme le ferait une page de type .html
. Le code HTML
est alors renvoyé au navigateur web. En revanche, si vous souhaitez que le serveur exécute du code PHP
avant de renvoyer le résultat au navigateur, il faut l'inclure entre les balises <?php instructions; ?>
.
Le score d'un joueur vaut initialement 0 et augmente à chaque fois que des cases sont fusionnées. Vous aurez donc besoin d'une variable qui peut augmenter à chaque action du joueur.
Créez une variable globale $score
initialisée à 0.
Une variable globale doit être déclarée au début du fichier php
qui utilise la variable. Dans votre cas, vous déclarerez les variables globales dans le fichier jeu-2048.php
avant d'inclure le fichier fontions-2048.php
car les fonctions vont également utiliser les variables globales.
Pour tester l'incrémentation de cette variable, vous allez créer une nouvelle fonction qui augmente le score de 1 à chaque fois que l'utilisateur clique sur le bouton Nouvelle partie
.
Dans votre fichier fontions-2048.php
, créez une fonction affiche_score()
qui incrémente le score de 1 si le bouton Nouvelle partie
est cliqué puis qui affiche le score dans la page HTML
qui sera renvoyée au navigateur web. Testez cette fonction en l'appelant dans le fichier jeu-2048.php
.
Quel score s'affiche si vous cliquez une fois sur Nouvelle partie
?
Quel score s'affiche si vous cliquez une nouvelle fois sur Nouvelle partie
? L'affichage du score est-il correct ? Comment expliquez-vous cette valeur ?
Pour utiliser une variable globale à l'intérieur d'une fonction, il faut la redéclarer au début de la fonction précédée du mot-clé global
.
Pour tester si l'utilisateur a cliqué sur le bouton Nouvelle partie
, utilisez la condition suivante :
if ($_GET['action-joueur'] == "Nouvelle partie") { instruction1; } else { instruction2; }
Plus tard dans le sujet, vous pourrez procéder de manière similaire pour vérifier si l'utilisateur a cliqué sur Haut
, Bas
, Droite
ou Gauche
.
Pour afficher le contenu de la variable $score
côté serveur avant ou après l'exécution de la fonction affiche_score()
ou pour afficher des messages de debug, utilisez les logs comme vous l'avez fait au TP précédent.
Vous avez dû constater que la variable $score
ne dépasse jamais la valeur 1 même si vous cliquez plusieurs fois de suite sur le bouton Nouvelle partie
. En affichant la valeur de $score
dans les logs avant de l'incrémenter, vous avez vu que le score repasse à 0 à chaque nouveau clic. En effet, après avoir incrémenté le score sur le serveur, sa valeur n'est pas conservée. Pour conserver la valeur du score entre deux actions successives, il est nécessaire de la stocker quelque part car le serveur ne conserve pas l'état des variables entre deux requêtes.
Pour conserver la valeur du score, vous utiliserez un fichier texte. Avant chaque incrémentation, vous allez lire la valeur du score dans ce fichier texte, la modifier dans votre programme, puis écrire la nouvelle valeur dans le fichier pour la retrouver à l'action suivante.
Créez un fichier texte score.txt
avec un 0
dedans. Pour cela, placez-vous dans le dossier jeu-2048
et tapez la commande echo 0 > score.txt
. Vérifiez que le contenu est correct avec la commande cat
.
Ajoutez une fonction score_vers_fichier()
qui copie la valeur de $score
dans score.txt
.
Ajoutez une fonction fichier_vers_score()
qui copie dans $score
la valeur lue dans score.txt
.
Modifiez votre fonction affiche_score()
pour lire le nombre stocké dans score.txt
avant de l'incrémenter, puis pour écrire la nouvelle valeur dans le même fichier. Vous utiliserez les deux fonctions précédentes.
Vérifiez que le score s'incrémente bien plusieurs fois lorsque vous cliquez plusieurs fois sur le bouton Nouvelle partie
.
Pour lire le contenu d'un fichier, utilisez la fonction file_get_contents($fichier);
.
Cette fonction retourne une chaîne de caractères remplie avec le contenu de tout le fichier $fichier
.
Pour écrire le contenu de $variable
dans le fichier $fichier
, vous pouvez utiliser la fonction file_put_contents($fichier, $variable);
.
Si le fichier contenait déjà quelque chose, l'ancien contenu est remplacé par $variable
.
Pour conserver l'ancien contenu et ajouter $variable
à la fin du fichier, il faut faire : file_put_contents($fichier, $variable, FILE_APPEND);
Contrairement au langage C, les variables n'ont pas de type prédéfini en PHP. Il est possible d'ajouter 1 à une chaîne de caractères contenant un nombre.
Votre grille ne contient pour l'instant que des valeurs statiques : chaque case contient la valeur 0
. Pourtant, les valeurs contenues dans la grille vont évoluer à chaque action de jeu. Comme pour le score, il va falloir conserver les valeurs de la grille dans un fichier texte côté serveur : grille.txt
. Pour manipuler la grille dans votre programme, vous utiliserez une variable globale $grille
qui sera une matrice, c'est à dire un tableau 4*4
à deux dimensions. Vous pourrez donc accéder à la valeur de la case i,j
en utilisant $grille[$i][$j]
où $i
et $j
sont les coordonnées de la case dans la grille, $i
et $j
variant entre 0 et 3.
Une case vide dans la grille sera représentée par la valeur 0
à la fois dans la variable $grille
et le fichier grille.txt
. En revanche, lors de l'affichage de la grille dans le navigateur du joueur, vous n'afficherez rien quand la valeur de la case est 0
.
Pour stocker la grille dans grille.txt
, utilisez le format suivant :
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Déclarez dans jeu-2048.php
une variable globale $grille
.
Ajoutez dans fonctions-2048.php
une fonction nouvelle_partie()
qui initialise le score à 0
et la grille comme un tableau 2D rempli avec la valeur 0
dans toutes les cases.
Ajoutez une fonction matrice_vers_fichier()
qui copie dans grille.txt
les valeurs stockées dans $grille
. Vous utiliserez deux boucles for
imbriquées.
Ajoutez une fonction fichier_vers_matrice()
qui copie dans $grille
les valeurs stockées dans grille.txt
.
Dans grille.txt
, faut-il mettre un espace après le dernier nombre de chaque ligne ? Que va faire la fonction fichier_vers_matrice()
si tel est le cas ? Modifiez la fonction matrice_vers_fichier()
en conséquence si nécessaire.
Ajoutez une fonction affiche_case($i,$j)
qui affiche avec echo
le contenu de la case $i,$j
. Si le contenu est égal à 0
, vous n'afficherez rien.
Testez les quatre fonctions que vous venez d'écrire :
Dans jeu-2048.php
. si l'utilisateur clique sur Nouvelle partie
, il faut exécuter la fonction nouvelle_partie()
sinon il faut exécuter fichier_vers_matrice()
.
Appelez la fonction matrice_vers_fichier()
à la fin de jeu-2048.php
.
Pour tester la fonction affiche_case($i,$j)
, modifiez le code HTML qui affiche la grille dans jeu-2048.php
. Vous utiliserez deux boucles for
imbriquées dans lesquelles vous appelerez affiche_case($i,$j)
.
Rappel : pour utiliser une variable globale à l'intérieur d'une fonction, il faut la redéclarer au début de la fonction avec global
.
Rappel : en informatique, la numérotation commence généralement à 0
. La première case de la grille est $grille[0][0]
, la dernière est $grille[3][3]
.
Rappel : pour débugger, pensez à utiliser les logs comme vous l'avez vu au TP précédent.
La fonction array_fill($index,$num,$val)
permet de remplir un tableau avec $num
éléments où chaque élément vaut $val
et l'indice du premier élément est $index
. Par exemple, array_fill(0,4,array_fill(0,4,0));
permet de définir un tableau de quatre éléments, chaque élément étant lui-même un tableau de 4 éléments valant tous 0
.
La strucutre d'une boucle for
est :
for ($i=0; $i < 10; $i++) { instructions; }
Pour la fonction fichier_vers_matrice()
, il faut utiliser la fonction explode()
qui permet de récupérer dans un tableau les différents morceaux d'une chaine de caractères en précisant le séparateur des morceaux. Dans l'exemple qui suit, le séparateur est le caractère espace :
$chaine = "1 2 3 4 5 6"; $valeurs = explode(' ', $chaine); echo $valeurs[0]; // affiche "1" echo $valeurs[2]; // affiche "3"
Vous pouvez vous inspirer du code ci-dessous pour écrire la fonction fichier_vers_matrice()
:
// $chaine va contenir tout ce qu'il y a dans le fichier 'grille.txt' $chaine = file_get_contents('grille.txt'); // on remplace dans $chaine tous les sauts de ligne par des espaces $chaine = str_replace("\n", " ", $chaine); // $valeurs est un tableau 1D qui va contenir tous les nombres de la grille $valeurs = explode(' ', $chaine); $n = 0; for ($i = 0; $i < 4 ; $i++) { for ($j = 0; $j < 4; $j++) { $grille[$i][$j] = (int) ($valeurs[$n]); $n++; } }
Au début de la partie, après un clic sur Nouvelle partie
, il faut remettre le score à 0
(déjà fait), initialiser la grille (déjà fait), et faire apparaître deux fois le nombre 2
dans la grille.
Pour placer ces deux nombres 2
, il faut tirer aléatoirement leur position dans la grille.
Dans fonctions-2048.php
, ajoutez une fonction tirage_position_vide()
qui retourne un tableau de deux éléments i
et j
correspondant aux coordonnées de la case de la grille qui va accueillir un nouveau nombre. Les valeurs i
et j
doivent être tirées aléatoirement entre 0 et 3 jusqu'à obtenir une position correspondant à une case vide. Vous supposerez que cette fonction n'est appelée que s'il reste au moins une case vide.
Dans la fonction nouvelle_partie()
, mettez deux fois le nombre 2
dans $grille
. Vous appelerez tirage_position_vide()
pour placer chaque nombre. Testez que tout fonctionne correctement en cliquant plusieurs fois sur le bouton Nouvelle partie
.
Pour tirer un nombre aléatoirement, on utilise la fonction rand(borne_inf, borne_sup)
. Pour faire le tirage jusqu'à obtenir une case vide, vous utiliserez une boucle while (condition) { instructions; }
Par ailleurs, après chaque action du joueur, il faut tirer aléatoirement un nombre dont la valeur est soit 2
, soit 4
, ce nombre devant être placé aléatoirement dans une case vide de la grille, à condition que la grille ne soit pas pleine.
Ajoutez une fonction grille_pleine()
qui retourne true
si la grille est pleine, false
sinon. La grille est pleine si elle ne contient plus de zéro.
Ajoutez une fonction tirage_2ou4()
qui retourne aléatoirement la valeur 2
ou 4
.
Ajoutez une fonction place_nouveau_nb()
qui fait appel à tirage_2ou4()
et tirage_position_vide()
pour placer un nouveau nombre 2
ou 4
dans une case vide de $grille
, le nombre et la case étant tirés aléatoirement.
Si un bouton de direction est cliqué, appelez la fonction place_nouveau_nb()
à condition que la grille ne soit pas pleine. Si la grille est pleine, affichez un message au joueur pour lui dire que la partie est perdue. Testez que tout fonctionne correctement en appuyant sur les boutons de direction plusieurs fois jusqu'à remplir la grille et perdre la partie.
Pour tirer aléatoirement la valeur 2
ou 4
, vous tirerez aléatoirement un nombre entre 1
et 2
et vous le multiplierez par 2
.
A ce stade, votre programme permet de démarrer une nouvelle partie, de stocker l'état de la grille sur le serveur, d'ajouter dans la grille un nouveau nombre lors de chaque action de jeu et de déclarer la partie perdue lorsque la grille est pleine.
Lorsque le joueur clique sur un bouton de direction, il faut commencer par décaler ou tasser tous les nombres de la grille dans le sens indiqué par le joueur. Par exemple, si le joueur clique sur Gauche
, sur chaque ligne, il faut décaler tous les nombres vers la gauche en ne laissant aucune case vide entre deux nombres. Rappelez-vous que les cases vides sont représentées par des zéros. Voici un exemple de décalage vers la gauche :
Avant Après 0 2 0 4 2 4 0 0 4 4 0 2 4 4 2 0 0 0 4 4 4 4 0 0 0 2 2 0 2 2 0 0
Dans fonctions-2048.php
, ajoutez une fonction decale_ligne_gauche($l)
qui réalise le décalage vers la gauche de la ligne $l
.
Pour faire un décalage de toutes les lignes vers la gauche, il faut appeler quatre fois la fonction précédente. Testez que le décalage vers la gauche fontionne bien en utilisant une boucle for
.
Rappel : en informatique, la numérotation commence généralement à 0
donc $l
varie entre 0
et 3
.
Pour faire le décalage, vous pouvez utiliser un tableau temporaire $ligne
qui va stocker la nouvelle ligne après décalage :
$ligne = array_fill(0,4,0); $i = 0; for ($j = 0; $j < 4; $j++) { if ($grille[$l][$j] != 0) { $ligne[$i] = $grille[$l][$j]; $i++; } } $grille[$l] = $ligne;
Vous venez de traiter le décalage vers la gauche. Le décalage vers la droite est similaire sauf qu'il faut traiter la ligne dans l'autre sens. Le décalage vers le haut ou le bas est similaire également sauf qu'il faut procéder par colonne au lieu de le faire par ligne.
En vous inspirant du décalage à gauche, ajoutez les fonctions decale_ligne_droite($l)
, decale_col_haut($c)
et decale_col_bas($c)
.
Testez que les décalages fontionnent bien dans tous les sens. Pour appliquer le décalage aux quatre lignes ou colonnes de la grille, vous utiliserez une boucle for
dans laquelle vous insérerez un switch
pour appeler la bonne fonction selon le bouton de direction choisi par le joueur.
Rappel : pour débugger, pensez à utiliser les logs comme vous l'avez vu au TP précédent.
Pour faire le décalage en colonne, vous ne pourrez pas faire $grille[$c] = $colonne;
comme cela a été fait pour les lignes car $grille[$c]
n'est pas une colonne. A la place, il faut utiliser une boucle for
pour recopier les valeurs de $colonne
dans $grille
.
Voici la structure d'un switch
:
switch ($variable) { case 'gauche': // instructions exécutées si $variable == 'gauche'; break; case 'droite': // instructions exécutées si $variable == 'droite'; break; // on peut mettre autant de case que l'on veut }
Après avoir fait le décalage dans la direction choisie par le joueur, il faut faire la sommation des cases adjacentes qui ont une valeur identique. Nous appelerons fusion
cette opération. Il ne peut pas y avoir de fusions multiples : une case qui est le résultat d'une fusion ne peut pas fusionner de nouveau lors de cette action de jeu. Si deux cases fusionnent, une case contient la somme des deux cases fusionnées, l'autre case est laissée vide.
Comme pour le décalage, la fusion sera effectuée sur la ligne $l
ou la colonne $c
. Comme pour le décalage, vous allez faire une fonction de fusion différente pour chacune des quatre directions.
Voici deux exemples de fusion vers la gauche qui regroupent la plupart des cas possibles :
Exemple 1 Exemple 2 Avant Après Avant Après 2 4 0 0 2 4 0 0 4 8 4 4 4 8 8 0 4 4 2 0 8 0 2 0 2 2 4 4 4 0 8 0 4 2 2 4 4 4 0 4 2 2 2 0 4 0 2 0 4 2 2 2 4 4 0 2 2 2 2 2 4 0 4 0
Après la fusion, il faut refaire un décalage pour éliminer les zéros qui sont apparus lors de la fusion.
Ajoutez une fonction fusion_ligne_gauche($l)
qui réalise la fusion vers la gauche des cases adjacentes de valeur égale sur la ligne $l
.
Pour faire la fusion sur toutes les lignes vers la gauche, il faut appeler quatre fois la fonction précédente. Testez que la fusion vers la gauche fontionne bien en utilisant une boucle for
.
Pour faire la fusion vers la gauche, vous pouvez utiliser le code ci-dessous :
if ($grille[$l][0] == $grille[$l][1]) { $grille[$l][0] = 2 * $grille[$l][0]; $grille[$l][1] = 0; if ($grille[$l][2] == $grille[$l][3]) { $grille[$l][2] = 2 * $grille[$l][2]; $grille[$l][3] = 0; } } else if ($grille[$l][1] == $grille[$l][2]) { $grille[$l][1] = 2 * $grille[$l][1]; $grille[$l][2] = 0; } else if ($grille[$l][2] == $grille[$l][3]) { $grille[$l][2] = 2 * $grille[$l][2]; $grille[$l][3] = 0; }
Vous venez de traiter la fusion vers la gauche. La fusion vers la droite est similaire sauf qu'il faut traiter la ligne dans l'autre sens. La fusion vers le haut ou le bas est similaire également sauf qu'il faut procéder par colonne au lieu de le faire par ligne.
En vous inspirant de la fusion à gauche, ajoutez les fonctions fusion_ligne_droite($l)
, fusion_col_haut($c)
et fusion_col_bas($c)
.
Testez que les fusions fontionnent bien dans tous les sens. Pour appliquer la fusion aux quatre lignes ou colonnes de la grille, vous appelerez les fonctions de fusion dans le switch
utilisé pour le décalage. N'oubliez pas qu'il faut refaire un décalage après la fusion pour éliminer les zéros qui sont apparus du fait de la fusion.
A ce stade, le jeu fonctionne. Bravo ! Il ne reste plus qu'à gérer le calcul du score et à donner une couleur différente aux cases de la grille selon la valeur du nombre contenu dans la case.
Concernant le score, vous disposez d'une variable globale $score
, d'un fichier score.txt
pour conserver le score entre deux actions du joueur et des fonctions score_vers_fichier()
et fichier_vers_score()
. La fonction affiche_score()
est utilisée pour afficher le score dans la page du jeu.
Dans le jeu 2048, le score initial vaut 0. Ensuite, lors de chaque fusion, on ajoute au score la somme des cases fusionnées. Par exemple, si le score vaut 0 et que l'on fusionne deux cases valant 2, le score passe à 4.
Modifiez les fonctions de fusion pour prendre en compte l'augmentation du score lors de chaque nouvelle fusion. Testez que le score s'affiche correctement à chaque nouvelle action de jeu.
Pour l'instant, toutes les cases ont la même couleur. Or, dans le jeu 2048, les cases sont de plus en plus foncées en fonction de leur valeur.
Il faut donc appliquer un style qui dépend de la valeur contenue dans la case. Pour cela, il faut d'une part définir un style différent pour chaque valeur possible dans la feuille de styles styles2048.css
et, d'autre part, appliquer ce style dans le code HTML généré par jeu-2048.php
.
Ajoutez dans styles2048.css
les styles permettant de définir une couleur de fond différente pour chaque valeur possible : 2
, 4
, 8
, 16
, 32
, 64
, 128
, 256
, 512
, 1024
, 2048
et la cellule vide.
Dans fonctions-2048.php
, modifiez la fonction affiche_case($i,$j)
pour qu'elle génére le code HTML permettant d'afficher la case de coordonnées [$i],[$j]
. Vous utiliserez un switch
pour faire un affichage différent selon la valeur contenue dans la case.
Testez que l'affichage est correct.
Si votre grille est un tableau HTML, voici ci-dessous un exemple de code pour la valeur 32
. Si vous avez choisi d'utiliser des blocs plutôt qu'un tableau HTML, il vous suffit de remplacer td
par div
.
switch ($grille[$i][$j]) { case 32: echo "<td class='c32'> valeur </td>"; break; }
Voici des exemples de couleur que vous pouvez utiliser :
Valeur 0 : #f5f5f5 Valeur 2 : #eeed4a Valeur 4 : #ede0c8 Valeur 8 : #f2b179 Valeur 16 : #f59563 Valeur 32 : #f67c5f Valeur 64 : #f65e3b Valeur 128 : #edcf72 Valeur 256 : #edcc61 Valeur 512 : #edc850 Valeur 1024 : #edc53f Valeur 2048 : #edc22e
Bravo, vous avez terminé le jeu 2048 ! Tant que la partie n'est pas perdue, le jeu continue même si le nombre 2048 a été atteint. Il s'agit alors d'atteindre le plus haut score possible.
Vérifiez que la fin de partie se passe comme prévu : la partie est perdue si la grille est pleine et qu'aucune fusion n'est possible. Si ce n'est pas le cas, modifiez votre code.
Plutôt que de faire 4 fonctions de décalage (une dans chaque direction) et 4 fonctions de fusion (idem), il aurait été préférable d'écrire une fonction decalage($tab)
et une fonction fusion($tab)
où $tab
est un tableau 1D qui contient la ligne ou la colonne à décaler/fusionner. Avant l'appel de ces fonctions, $tab
est rempli dans le bon ordre en tenant compte de la direction choisie par le joueur. Ces fonctions doivent retourner un nouveau tableau qui correspond au tableau décalé/fusionné.
Ecrivez les fonctions decalage($tab)
et fusion($tab)
puis modifiez jeu-2048.php
pour remplir $tab
selon la direction choisie, appeler ces fonctions et modifier la grille en conséquence grâce au tableau retourné par chaque fonction.
Entre deux séances de TP, vous avez au moins 15 jours pour :
Pour travailler depuis chez vous sur le serveur web que vous utilisez en TP, suivez les instructions sur cette page.