Jeu de Yams

Rappels d’utilisation des tableaux

  • Déclarer un tableau d’entiers tab :

vector<int> tab;
  • Allouer 5 cases au tableau tab :

tab = vector<int>(5);
  • Initialiser ses cases :

tab[0] = 42; tab[1] = 3; ... ; tab[4] = 36;
  • Déclarer, allouer et initialiser en une seule instruction :

vector<int> tab = {25,-3,10,7};
  • Il est également possible de construire un tableau sans lui donner de nom pour le passer à une fonction de la manière suivante :

maFonction({17,9,-3,42});
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;

typedef vector<int> tableau;

// Initialisation de la fonction rand (pour les nombres aléatoires) :
srand(time(0));

Nous vous conseillons fortement lorsque vous avez besoin de redémarrer le noyau avec Kernel > Restart (and clear all si vous voulez faire le ménage) de faire ensuite systématiquement la commande Cell > Run all above, car toutes les fonctions définies dans cette feuille seront utilisées à la fin.

Reconnaître les yams

pointsFigureYams

Complétez la documentation de la fonction pointsFigureYams puis implémentez-la dans la case suivante. Pour rappel, la fonction pointsFigureYams doit vérifier si les cinq valeurs de dés fournies forment un Yams ou non. Si un Yams est trouvé on renvoie le score correspondant (somme des dés + 60) sinon 0.

Attention, pour éviter un bug de l’interpréteur C++ cling, ne pas d’utiliser d’apostrophes (“) dans vos commentaires / documentations !

/** Fonction pointsFigureYams
 * @param 
 * @return 
 **/
/// BEGIN SOLUTION
int pointsFigureYams(vector<int> des) {
  // Variable locale :
  int val = des[0];  // Pour vérifier que tous les dés sont égaux au premier

  for(int i = 1; i < des.size(); i++) {
    if(val != des[i]) {
      // Si un  est différent du premier alors pas de yams
      return 0;
    }
  }
  
  return (val * 5 + 60); 
}
/// END SOLUTION

Complétez les tests de la cellule suivante avec aux moins deux tests puis exécutez les deux cellules pour vérifier que votre fonction pointsFigureYams a bien le comportement attendu.

CHECK( pointsFigureYams({1,1,1,1,1}) == 65 );
CHECK( pointsFigureYams({2,1,1,1,1}) ==  0 );
CHECK( pointsFigureYams({2,2,2,2,2}) == 70 );
CHECK( pointsFigureYams({3,3,3,3,3}) == 75 );
CHECK( pointsFigureYams({4,4,4,4,4}) == 80 );
CHECK( pointsFigureYams({5,5,5,5,5}) == 85 );
CHECK( pointsFigureYams({6,6,6,6,6}) == 90 );

Compilation et tests en dehors de Jupyter

  • Ouvrez le fichier yams.cpp fourni dans l’archive Semaine6 avec un éditeur de texte tel que jedit ou l’éditeur de texte de JupyterHub.

  • Copiez-collez votre fonction pointsFigureYams à un endroit adapté.

  • Enregistrez votre fichier yams.cpp

  • Compilez-le à l’aide de la commande

    g++ --std=c++11 yams.cpp -o yams
    

    Attention, pour qu’il n’y ait pas de message d’erreur il faut être allé dans le bon repertoire avec la commande cd (Ex. cd ~/ProgImperative/Semaine6).

  • Exécutez le programme ainsi généré à l’aide de la commande ./yams. Votre terminal doit afficher 65.

Faire sérieusement les questions de compilation pour vous y habituer: tout votre projet de fin de semestre devra être fait sous forme compilée.

Quelques utilitaires

aleaint

La fonction aleaint(int a, int b) proposée ci-dessous permet de tirer au hasard un nombre entier compris entre a et b :

/** Fonction aleaInt
 * @param a un entier représentant le minimum du nombre aléatoire généré
 * @param b un entier représentant le maximum du nombre aléatoire généré
 * @return un entier aléatoire n tel que a <= n <= b
 **/
int aleaint(int a, int b) {
    return rand() % (b - a + 1) + a;
}

lanceDes

Complétez la documentation de la fonction lanceDes puis implémentez-la dans la case suivante en utilisant aleaint. Pour rappel, la fonction lanceDes doit renvoyer un tableau contenant 5 valeurs tirées aléatoirement entre 1 et 6. (Comme la semaine dernière, pour contourner un bug dans cling on va mettre le type tableau à la place de vector<int>.)

/** Fonction lanceDes
 * @return 
 **/
tableau lanceDes() {
    /// BEGIN SOLUTION
    vector<int> t;
    t = vector<int>(5);

    for ( int i = 0; i < t.size(); i++ ) {
        t[i] = aleaint(1, 6);
    }

    return t;
    /// END SOLUTION
}

Pour tester la fonction lanceDes, il faudrait pouvoir voir son résultat, c’est-à-dire afficher le contenu d’un tableau. Pour afficher le contenu d’une variable, vous avez maintenant l’habitude d’utiliser cout, mais ceci ne fonctionne pas pour les tableaux, comme vous pouvez le voir en exéctuant la cellule ci-dessous. Après avoir exécuté et constaté le problème, mettez en commentaire le ligne contenant le cout (en mettant // en début de ligne) puis redémarrez le noyau.

vector<int> t = {5,-13,0,27};
cout << t << endl;

Pour pouvoir afficher le contenu d’un tableau, on va écrire la fonction afficheDes.

afficheDes

Complétez la documentation de la fonction afficheDes puis implémentez-la dans la cellule suivante. Pour rappel, cette fonction doit afficher les valeurs des dés qui lui sont donnés en entrée.

/** Fonction afficheDes
 * @param 
 **/
/// BEGIN SOLUTION
void afficheDes(vector<int> des) {
    for ( int i = 0; i < des.size(); i++ ) {
        cout << des[i] << ";";
    }
    cout << endl;
}
/// END SOLUTION

Pour tester votre fonctions afficheDes, exécuter la cellule suivante et vérifier l’affichage obtenu.

afficheDes({4, 21, -1, 2});

Testez votre fonction lanceDes en exécutant à plusieurs reprises la cellule suivante :

afficheDes(lanceDes());

compteDes

Après avoir complété sa spécification, implémentez la fonction compteDes vue en TD.

/** Fonction compteDes
 * @param 
 * @return 
 **/
tableau compteDes(vector<int> des) {
    /// BEGIN SOLUTION
    vector<int> res = {0, 0, 0, 0, 0, 0};
    for ( int i = 0; i < des.size(); i++ ) {
        int indice = des[i] - 1;
        res[indice] += 1;
    }
    return res;
    /// END SOLUTION
}

Vérifiez la correction de votre fonction compteDes en exécutant les tests suivants vus en TD :

CHECK( compteDes({1,1,1,1,1}) == vector<int>({5, 0, 0, 0, 0, 0}) );
CHECK( compteDes({2,2,2,2,2}) == vector<int>({0, 5, 0, 0, 0, 0}) );
CHECK( compteDes({3,3,3,3,3}) == vector<int>({0, 0, 5, 0, 0, 0}) );
CHECK( compteDes({4,4,4,4,4}) == vector<int>({0, 0, 0, 5, 0, 0}) );
CHECK( compteDes({5,5,5,5,5}) == vector<int>({0, 0, 0, 0, 5, 0}) );
CHECK( compteDes({6,6,6,6,6}) == vector<int>({0, 0, 0, 0, 0, 5}) );
CHECK( compteDes({1,2,3,4,5}) == vector<int>({1, 1, 1, 1, 1, 0}) );
CHECK( compteDes({2,2,6,2,2}) == vector<int>({0, 4, 0, 0, 0, 1}) );
CHECK( compteDes({4,1,4,1,1}) == vector<int>({3, 0, 0, 2, 0, 0}) );

chercheDansTableau

Au tour maintenant de chercheDansTableau qui étant donné un entier et un tableau cherche si cet entier est présent ou non dans le tableau, renvoie son indice s’il est présent, et -1 sinon.

/** Fonction chercheDansTableau
 * @param 
 * @param 
 * @return 
 **/
/// BEGIN SOLUTION
int chercheDansTableau(int n, vector<int> tab) {
    for ( int i = 0; i < tab.size(); i++ ) {
        if ( tab[i] == n ) {
            return i;
        }
    }
    // Si on n'a pas trouvé l'entier on renvoie -1 :
    return -1;
}
/// END SOLUTION

Complétez les tests de la cellule suivante et exécutez-les pour vérifier la correction de votre fonction chercheDansTableau.

CHECK( chercheDansTableau( 3, {42,5,3,6,7}) ==  2);
CHECK( chercheDansTableau(56, {42,3,5,6,7}) == -1);
/// BEGIN SOLUTION
CHECK( chercheDansTableau( 2, {1,2,3,4,5,6}) ==  1);
CHECK( chercheDansTableau(99, {4,7,3,2,9  }) == -1);
/// END SOLUTION

Main

Écrivez ci-dessous les instructions pour :

  • Tirer 5 dés aléatoirement et stocker le résultat dans la variable tirage.

  • Afficher le résultat du tirage.

  • Afficher le nombre de points obtenus en cherchant un Yams dans ce tirage.

Testez votre chance en exécutant plusieurs fois la cellule !

vector<int> tirage;
/// BEGIN SOLUTION
tirage = lanceDes();
afficheDes(tirage);
cout << "Vous marquez " << pointsFigureYams(tirage) << " points." << endl;
/// END SOLUTION

Compilation et test en dehors de Jupyter

  • Ouvrez votre fichier yams.cpp.

  • Copiez-collez les fonctions afficheDes, lanceDes, compteDes et chercheDansTableau à un endroit adapté.

  • Remplacez la ligne d’affichage contenue dans la fonction main par le code des deux cellules précedentes.

  • Enregistrez votre fichier yams.cpp

  • Compilez-le à l’aide de la commande g++ --std=c++11 yams.cpp -o yams

  • Exécutez le programme ainsi généré : ./yams. Le résultat est-il bien celui attendu ?

Les autres figures

pointsFigureBrelan

Voici une fonction pointsFigureBrelan un peu différente de celle vue en TD :

/** Fonction pointsFigureBrelan
 * @param des un tableau de 5 entiers (les dés)
 * @return 0 si aucun brelan est trouvé, la somme des dés formant le brelan + 10 sinon
 **/
int pointsFigureBrelan(vector<int> des) {
    vector<int> compte = compteDes(des);
    
    int valeur = (chercheDansTableau(3, compte) + 1)
               + (chercheDansTableau(4, compte) + 1)
               + (chercheDansTableau(5, compte) + 1);

    if (valeur > 0) 
        return (valeur * 3) + 10;
    else
        return 0;
}

Analysez son fonctionnement puis écrivez quelques tests pour vérifier qu’elle se comporte comme prévu :

/// BEGIN SOLUTION
CHECK( pointsFigureBrelan({4,4,4,1,1}) == 22 );
CHECK( pointsFigureBrelan({1,1,4,4,5}) ==  0 );
CHECK( pointsFigureBrelan({1,1,1,1,5}) == 13 );
/// END SOLUTION

pointsFigureCarre & pointsFigureFull

Sur le même modèle, donnez deux fonctions pointsFigureCarre et pointsFigureFull répondant aux documentations ci-dessous. Rappel: Full: 3 dés identiques et 2 dés identiques, Carré: 4 dés identiques.

/** Fonction pointsFigureCarre
 * @param des un tableau de 5 entiers (les dés)
 * @return 0 si aucun carre (4 dés identiques) est trouvé, la somme des dés formant le carré + 30 sinon
 **/
/// BEGIN SOLUTION
int pointsFigureCarre(vector<int> des) {
  // On commence par conmpter les dés :
  vector<int> compte = compteDes(des);

  // On cherche si on a un carré ou un yams :
  int val = (chercheDansTableau(4, compte) + 1) + (chercheDansTableau(5, compte) + 1);

  if(val > 0)
    return (val * 4 + 30);
  
  // Si on ne trouve pas de brelan on renvoie 0 :
  return 0;
}
/// END SOLUTION
/** Fonction pointsFigureFull,
 * @param des un tableau de 5 entiers contenant les valeurs des dés
 * @return 0 si pas de full, somme des dés composant le full + 20 si full il y a
 **/
/// BEGIN SOLUTION
int pointsFigureFull(vector<int> des) {
  // On commence par conmpter les dés :
  vector<int> compte = compteDes(des);

  // On regarde si on a une paire et un brelan :
  int valPaire = chercheDansTableau(2, compte) + 1;
  int valBrelan = chercheDansTableau(3, compte) + 1;

  if(valPaire > 0 and valBrelan > 0)
    return (valPaire * 2 + valBrelan * 3 + 20);
  // Si on ne trouve pas de brelan on renvoie 0 :
  return 0;
}
/// END SOLUTION

Testez-les :

CHECK( pointsFigureFull({4,4,4,1,1}) == 34 );
CHECK( pointsFigureFull({1,1,4,4,5}) ==  0 );

CHECK( pointsFigureCarre({4,4,4,4,1}) == 46 );
CHECK( pointsFigureCarre({4,4,4,2,5}) ==  0 );

pointsFigure

Nous vous donnons la fonction pointsFigure vue en TD qui a pour but de chercher la figure choisie par l’utilisateur (via une chaîne de caractères) et de retourner le nombre de points ainsi obtenu.

/** Fonction pointsFigure,
 * @param des un tableau de 5 entiers contenant les valeurs des dés
 * @param figure une chaine de caractères contenant le nom de la figure a chercher
 * @return 0 si figure introuvable ou inconnue, le score correspondant sinon
 **/
int pointsFigure(vector<int> des, string figure) {
    if        ( figure == "brelan" ) {
      return pointsFigureBrelan(des);
    } else if ( figure == "full"   ) {
        return pointsFigureFull(des);
    } else if ( figure == "carre"  ) {
        return pointsFigureCarre(des);
    } else if ( figure == "yams"   ) {
        return pointsFigureYams(des);
    } 
    return 0;
}

Compilation et test en dehors de Jupyter

  • Ouvrez votre fichier yams.cpp.

  • Copiez-collez les fonctions pointsFigureCarre et pointsFigureFull à un endroit adapté.

  • Décommentez les fonctions pointsFigureBrelan et pointsFigure.

  • Enregistrez votre fichier yams.cpp

  • Vérifiez qu’il compile toujours à l’aide de la commande g++ --std=c++11 yams.cpp -o yams

  • Exécuter le programme tel quel ne changera rien à l’exécution précédente car nous n’avons pas modifié la fonction main, c’est ce que nous allons faire dans la partie suivante.

Boucle de jeu : la fonction main

La boucle de jeu, infinie, se décompose de la façon suivante :

Avant la boucle :

  • Déclaration des variables permettant de stocker la valeur des dés, le score et la reponse écrite du joueur.

Pendant la boucle :

  • On lance les dés et on stocke le résultat dans la variable appropriée.

  • On affiche le résultat du lancé à l’utilisateur et on demande quelle figure il choisit.

  • On boucle tant que l’utilisateur donne une réponse qui n’est ni une figure (brelan, full, carre, yams) ni exit.

  • Si l’utilisateur a répondu exit on sort de la boucle principale avec l’instruction return 0;

  • Sinon, c’est que l’utilisateur a donné le nom d’une figure, on appelle donc pointsFigure de façon à récupérer le nombre de points correspondant à cette figure et à ce lancer de dés. On ajoute ces points au score du joueur, et on affiche le résultat au joueur.

Dans le Jupyter Notebook on ne peut pas utiliser « cin » pour demander quelque chose à l’utilisateur, on simulera donc les boucles par une exécution multiple des cellules concernées. Votre objectif est de compléter le corps de la boucle ci-dessous, puis de tester le jeu en exécutant les cellules dans l’ordre approprié.

/* AVANT LA BOUCLE PRINCIPALE */

int pts, score = 0;
string reponse = "";
vector<int> des;

/* BOUCLE PRINCIPALE */

On lance les dés et on stocke le résultat dans la variable appropriée :

/// BEGIN SOLUTION
des = lanceDes();
/// END SOLUTION

On affiche le résultat du lancé au joueur et on affiche un texte demandant quelle figure il choisit :

/// BEGIN SOLUTION
afficheDes(des);
cout << "Quelle figure choisissez vous ? (brelan, carre, full ou yams, exit pour quitter)" << endl;
/// END SOLUTION

On boucle tant que la réponse de l’utilisateur n’est pas brelan, carre, full ou yams ni exit. Ici, on considère que le joueur modifie directement la cellule suivante pour indiquer son choix (mais dans le programme compilé on récupèrera le choix avec cin) :

reponse = "brelan";

Si l’utilisateur a répondu exit on affiche « Fin du programme » dans Jupyter (mais dans le programme compilé il faudra penser à sortir de la boucle principale avec l’instruction return 0;).

if (reponse == "exit") cout << "Fin du jeu, votre score final est : " << score << endl;

Sinon, c’est que l’utilisateur a donné le nom d’une figure, on appelle alors pointsFigure de façon à récupérer le nombre de points correspondant à cette figure et à ce lancer de dés. On ajoute ces points au score du joueur, et on affiche le résultat au joueur.

/// BEGIN SOLUTION
pts = pointsFigure(des, reponse);
score += pts;
cout << "Vous marquez " << pts << " points, votre score total est de " << score << " points." << endl; 
/// END SOLUTION

/* FIN DE LA BOUCLE PRINCIPALE */

Compilation et test en dehors de Jupyter

Enfin, en vous basant sur le code de cette partie de la fiche, la fin du TD et le squelette suivant, complétez la fonction main de votre fichier yams.cpp afin de pouvoir jouer au jeu en l’exécutant (après l’avoir compilé) depuis votre terminal. (Si jamais vous entrez dans une boucle infinie, vous pouvez stopper votre programme à tout moment en pressant les touches ctrl+c)

int main() {
  // Initialisation de la fonction rand (pour les nombres aléatoires) :
  srand(time(0));

  // AVANT LA BOUCLE PRINCIPALE
  // On déclare les variables utilisées par la boucle principale :
  // VOTRE CODE ICI
  
  // BOUCLE PRINCIPALE
  while (true) {
    // On lance les dés et on stocke le résultat dans la variable appropriée :
    // VOTRE CODE ICI
    
    // On affiche le résultat du lancé au joueur et on affiche un texte demandant quelle figure il choisit :
    // VOTRE CODE ICI

    // On boucle tant que la réponse de l'utilisateur n'est pas brelan, carre, full ou yams ni exit : 
    do {
      cin >> reponse;
    } while (reponse != "brelan" and reponse != "full" 
	     and reponse != "carre" and reponse != "yams" and reponse != "exit");

	// Si la réponse est exit on quitte l'application :
    if(reponse == "exit") { 
        return 0; 
    }
    
    // Si l'utilisateur a choisi une figure on calcule le nouveau score et on l'affiche :
    // VOTRE CODE ICI
  }
  // FIN DE LA BOUCLE PRINCIPALE
    
  return 0;
}

De la suite dans les idées

Dans le jeux de Yams complet d’autres figures sont reconnues comme la Grande Suite (5 dés qui se suivent comme 2,3,4,5,6) et la Petite Suite (4 dés qui se suivent comme 2,3,4,5). Attention, l’ordre des dés n’a pas d’importance, les dés 2,4,3,1,5 forment donc une grande suite, même s’ils sont dans le désordre !

Donner deux fonctions répondant aux spécifications ci-dessous et vérifier que vos implémentations passent les tests:

/** Fonction pointsFigureGrandeSuite
 * @param des un tableau de 5 entiers (les dés)
 * @return 0 si aucune grande suite (5 dés qui se suivent) n est trouvée, 
 *   la somme des dés formant la grande suite  + 50 sinon
 **/
/// BEGIN SOLUTION
int pointsFigureGrandeSuite(vector<int> des) {
    vector<int> compte = compteDes(des);
    
    // Seulement 2 grandes suites possibles :
    if(compte == vector<int>({0,1,1,1,1,1}) || compte == vector<int>({1,1,1,1,1,0})) {
        int somme = 0;
        for(int i = 0; i < des.size(); i++) somme += des[i];
        return somme + 50;
    }
    return 0;
}
/// END SOLUTION
CHECK( pointsFigureGrandeSuite({4,3,2,1,5}) == 15+50 );
CHECK( pointsFigureGrandeSuite({2,3,4,5,6}) == 20+50 );
CHECK( pointsFigureGrandeSuite({2,3,4,5,2}) ==     0 );
/** Fonction pointsFigurePetiteSuite
 * @param des un tableau de 5 entiers (les dés)
 * @return 0 si aucune petite suite (4 dés qui se suivent) n est trouvée, 
 *   la somme des dés formant la petite suite  + 40 sinon
 **/
/// BEGIN SOLUTION
int pointsFigurePetiteSuite(vector<int> des) {
   vector<int> compte = compteDes(des);
   int nbSuite = 0;
   int sommeSuite = 0;
   for(int i = compte.size()-1; i >= 0; i--) {
       if ( compte[i] > 0 ) {
           nbSuite++;
           sommeSuite += i + 1;
           if ( nbSuite == 4 ) {
               return sommeSuite + 40;
           }
       } else { // suite brisée
           nbSuite = 0;
           sommeSuite = 0;
       }
   }
   return 0;
}
/// END SOLUTION
CHECK( pointsFigurePetiteSuite({2,3,4,5,2}) == 14+40 );
CHECK( pointsFigurePetiteSuite({3,3,4,5,6}) == 18+40 );
CHECK( pointsFigurePetiteSuite({2,3,4,2,2}) == 0 );
CHECK( pointsFigurePetiteSuite({2,3,2,5,4}) == 14+40 );
CHECK( pointsFigurePetiteSuite({2,6,4,5,3}) == 18+40 );

Modifiez maintenant la boucle de jeu réalisée un peu plus haut afin de prendre en compte ces deux nouvelles figures. Testez-là en executant les cellules dans le bon ordre.

Compilation et test en dehors de Jupyter

  • Ajoutez à votre fichier yams.cpp les fonctions que vous venez de réaliser.

  • Modifiez la fonction main en conséquence.

  • Compilez votre programme et testez-le en l’exécutant.