TP 8 : Réalisation d'un "Catch me if you can" et Projet associé

C++11 sous linux au département, oui c'est possible!

Même si la distribution Debian du département commencent à dater, il est quand même possible d'avoir le compilateur g++ en version 4.8, et donc avoir accès aux fonctionnalités de C++11.
Pour cela, nous devons remercier M. Grousset Victor (étudiant en 2° année au département). Le mini-tutoriel (pour ce TP - pas pour la maison) est le suivant :

  1. Télécharger l'archive suivante;
  2. Décompresser la dans votre home_dirrectory (mais surtout pas dans votre net-home);
  3. Modifier votre fichier .bashrc (ou votre fichier .profile) de façon à obtenir l'alias g++11 qui pointe vers /home/ACRO/gcc-4.8.2/bin/g++
En conséquence, la nouvelle directive de compilation est la suivante:
		g++11 -Wall -std=c++11 [-o MyExe] MyCppFile.cxx
	

Mettre de la couleur dans le terminal sous GNU / Linux

Puisque les cours d'IHM (Interface Homme Machine) ne commencent qu'au second semestre, nous allons voir comment nous pouvons spécifier, dans un programme, la couleur des caractères ainsi que celle du fond d'une console, mais aussi effacer son contenu.
Puisque la console est une émulation d'un terminal VT100, toutes les instructions que nous allons lui donner commencent par \033[. Ensuite, une autre séquence d'instruction, concaténée à la première, provoque l'effacement de l'écran, le changement de la couleur de la police ou du fond.

Remarque : les informations évoquées dans cette partie fonctionnent aussi sous Mac OS-X, mais pas sous Windows.

Effacer l'écran

Afin d'effacer l'écran, la seconde séquence est : H\033[2J. En conséquence, la fonction suivante permet d'effacer l'écran :

void ClearScreen ()
{
    cout << "\033[H\033[2J";
}

Copiez la, et mettez la dans votre espace de nom anonyme.

Changer la couleur

Afin de changer la couleur, la seconde séquence est : XXm, où m est le code de la couleur souhaitée. Ce code est constitué de 2 chiffres comme le montre le tableau suivant :

Couleur Code associé
0 réinitialise le système de couleur à la valeur par défaut du shell
30 Noir
31 Rouge
32 Vert
33 Jaune
34 Noir
35 Magenta
36 Cyan

Copiez le code suivant, et mettez le dans votre espace de nom anonyme :
const string KReset   ("0");
const string KNoir    ("30");
const string KRouge   ("31");
const string KVert    ("32");
const string KJaune   ("33");
const string KBleu    ("34");
const string KMAgenta ("35");
const string KCyan    ("36");
	
void Couleur (const string & coul)
{
    cout << "\033[" << coul <<"m";
}	
Couleur (KRouge);
cout << "Rouge" << endl;
Couleur (KVert);
cout << "Vert" << endl;
Couleur (KReset);
cout << "Retour à la normale" << endl;

provoquera l'affichage :

Rouge
Vert
Retour à la normale

Changer le fond

Pour changer le fond, c'est exactement la même séquence d'instructions que pour changer la couleur. La seule différence provient du fait que les couleurs associées voient leur code respectif augmenté de 10.

Pour des informations complémentaires quant à la gestion du terminal (sous GNU / Linux), vous pouvez visiter cette page.

Gestion de la grille

Le but du projet est de réaliser une version simplifiée du célèbre jeu "voleur / contrebandier" (base du célèbre PacMan). Les règles du jeu sont les suivantes :

  1. Chaque joueur commence à deux coins diamétralement opposés de la grille. Dans notre cas, le Joueur1 débute en haut à droite et le Joueur2 en bas à gauche.
  2. Chaque joueur joue à tour de rôle, et ne peut faire qu'un unique déplacement, et d'une seule case. Les touches valides de déplacement sont 'A' (haut gauche), 'Z' (haut), 'E' (haut droit), 'Q', 'D', 'W', 'X, et 'C' (mais elles peuvent être changées).
  3. Le jeu s'arrête soit :
    • lorsqu'un joueur atteint la même position dans la grille qu'un autre;
    • lorsqu'un nombre pré-déterminé de coups a été joué. Dans ce cas, il y a match nul.
La grille est représentée par une matrice de taille MxN. Dans les paragraphes suivants, nous considérons que la matrice est carrée. En conséquence, nous pouvons définir les alias suivants :
typedef vector <char> CVLine; // un type représentant une ligne de la grille
typedef vector <CVLine> CMatrix; // un type représentant la grille
typedef pair   <unsigned, unsigned> CPosition; // un type représentant une coordonnée dans la grille		
De plus, les constantes globales (i.e. visibles par toutes les fonctions) suivantes sont définies :
const char KTokenPlayer1 = 'X';
const char KTokenPlayer2 = 'O';
const char KEmpty        = ' ';
Par souci d'uniformité, pour ce TP, le caractère :
  1. 'X' symbolise le pion du joueur 1 (la couleur est à votre guise);
  2. '0' désigne le pion du joueur 2 (la couleur est à votre guise);
  3. ' ' matérialise une case vide.

Affichage de la grille

Travail à effectuer : écrivez le corps de la fonction ShowMatrix () de profil :

void  ShowMatrix (const CMatrix & Mat)
Cette fonction doit :
  1. faire appel à la fonction ClearScreen ();
  2. remettre la couleur des caractères à sa valeur par défaut;
  3. afficher case / case le contenu de la case courante. Si la case n'est pas occupé par un joueur, on affiche une case vide, sinon, on affiche la couleur du joueur (définie auparavant) suivie du jeton.

Le jeu

Initialisation de la grille

Ecrire la fonction InitMat () de profil :

void InitMat (CMatrix & Mat, unsigned NbLine, unsigned NbColumn, CPosition & PosPlayer1, CPosition & PosPlayer2)
Cette fonction a pour but d'initialiser toutes les cases de la grille avec le caractère ' ' ainsi que de mettre le jeton du Joueur1 en haut à droite et celui du Joueur2 en bas à gauche.

Placement d'un jeton au bon endroit

Ecrire la fonction MoveToken () de profil :
void MoveToken (CMatrix & Mat, char Move, CPosition  & Pos)
Cette fonction déplace, dans la grille, le jeton situé initialement à la position Pos en fonction de la valeur du caractère Move, puis met à jour la position.

L'algorithme principal

Le but de cette partie est d'écrire la fonction Run () (fonction qui nous sert de programme principal) qui gère la partie. Cette fonction a pour profil :

int Run ()	
Dans le main (), vous devez uniquement appeler cette fonction. Cette fonction :
  1. initialise la matrice (grille) du jeu ainsi que le nombre de coup maximal;
  2. affiche la grille;
  3. entre dans une boucle tant qu'on peut jouer et qu'il n'y a pas de victoire;
    1. saisit le caractère correspondant à un déplacement;
    2. place le pion au bon endroit.
      Attention, aucun contrôle quant à la possibilité d'effectuer le déplacement de la grille n'est effectué.
    3. affiche la grille;
    4. effectue le test ;
  4. teste dans quel(s) état(s) on est sorti de boucle (s'il y a eu une victoire ou un match nul) et affiche les informations en conséquence.

Précision sur l'instruction "tant qu'on peut jouer" et conséquences

Puisque la grille contient M*M cases, il arrive fréquemment qu'aucun des joueurs ne prenne le dessus sur son adversaire. En conséquence, il faut limiter le nombre de mouvements des 2 joueurs.

Lecture d'un fichier de configuration

Puisque le jeu (tel qu'il est proposé) n'est pas très malléable, on se propose de lire un fichier de configuration qui doit contenir les informations suivantes :

  1. Nombre de lignes;
  2. Nombre de colonnes;
  3. Position des deux joueurs.
Les balises du fichier de configuration doivent obligatoirement suivre ce format :
NomBalise: Valeur
Un exemple de fichier de configuration est donné ci-dessous :
NbLine: 10
NbCol: 10
XPosPlay1: 0
YPosPlay1: 9
XPosPlay2: 9
YPosPlay2: 0
Modifier la fonction InitMat () en InitMatFromFile () pour prendre en compte ces modifications. Le profil de cette fonction est le suivant :
	 void InitMatFromFile (CMatrix & Mat, const string & FileName, CPosition & PosPlayer1, CPosition & PosPlayer2)
FileName désigne le chemin depuis votre exécutable vers le fichier de configuration.

Voici l'exécutable que vous devez obtenir MyProject. Le fichier de configuration (nommé obligatoirement config.txt) est celui donné ci-dessus. Ce fichier doit être dans le même répertoire que l'exécutable pour que ce dernier fonctionne.

La documentation

Doxygen est un puissant outil permettant de générer automatiquement une partie de la documentation de votre projet de C++. Malheureusement, cet outil n'est pas complètement disponible sur la version de Debian du département (ma faute). En revanche, il est relativement complet sous Windows.
Une fois que vous êtes sûr que votre code est "fonctionnel", retourner sous Windows, ajouter les balises nécessaires en suivant cette aide. Veillez à bien activer les références croisées entre le code source et la sortie.
Faites des tests jusqu'à que ce votre fichier de sortie ressemble à celui-ci. Ce fichier est le fichier généré par la solution (partielle) du projet.

Le projet

Le fichier de solution est une ébauche de solution. Vous disposez d'un peu plus de 3 semaines pour y ajouter toutes les fonctionnalités que vous souhaitez (sans pour autant changer de langage de programmation).

Le rendu de projet

Remarque : pour ceux qui ont des connaissances supplémentaires (celles qu'on va vous enseigner en S2), vous pouvez utiliser un EDI ainsi que des fichiers séparés, mais cet ajout ne sera pas pris en compte lors de la notation du projet. En revanche, n'envoyez pas de binaire (exécutable) : votre mail a 99,99999% de chance de ne pas être reçu par vos enseignants. De même, ne perdez pas de temps à faire une IHM, ce n'est pas l'objectif.

Soutenance orale

Lors de votre soutenance, vous aurez 15 minutes (maximum) pour effectuer une présentation via diaporama afin d'expliquer quels sont les apports de votre projet par rapport à la version initialement proposée. S'en suivra une discussion de 15 mn.

Pénalités

La liste de pénalités suivante n'est pas exhaustive :

  1. Projet rendu en retard : -1 pt par heure d'envoi de l'email (les emails sont horodatés);
  2. Copie sur un autre groupe ( on ne savait pas comment implémenter telle ou telle fonctionnalité dont on avait besoin pour aller plus loin, on l'a donc copiée sur un autre groupe), plusieurs cas se distinguent :
    • si le groupe est clairement nommé, cette fonctionnalité ne sera pas prise en compte dans la notation;
    • si le groupe est clairement nommé, et qu'il y a une amélioration de la fonctionnalité, (note pour la fonctionnalité / 2 - seulement la moitié du travail a été réalisée);
    • 0 aux deux groupes sinon.
    Deux groupes peuvent développer la même fonctionnalité, mais ils ne doivent pas avoir la même implémentation, les mêmes jeux d'essais.