TP : le 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});
Astuce
Lorsque vous avez besoin de redémarrer le noyau, utilisez le menu
Noyau > Redémarrer le noyau et exécutez jusqu'à la cellule selectionnée
.
Ainsi vous disposez d’un environnement d’exécution propre avec toutes les fonctions précédentes bien définies, ainsi que les instructions d’initialisation qui suivent.
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;
using tableau = vector<int>;
// Initialisation de la fonction rand (pour les nombres aléatoires) :
srand(time(0));
Exercice 1 : Reconnaître les yam’s#
pointsFigureYams
#
Complétez la documentation de la fonction pointsFigureYams
ébauchée dans
la cellule suivante, puis implémentez cette fonction dans la cellule d’après.
Pour rappel, la fonction pointsFigureYams
doit vérifier si les cinq valeurs
de dés fournies forment un yam’s ou non. Si un yam’s est trouvé on renvoie
le score correspondant (somme des dés + 60), sinon on renvoie 0.
/** Fonction pointsFigureYams
* @param des
* @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 dé est différent du premier alors pas de yams
return 0;
}
}
return (val * 5 + 60);
}
/// END SOLUTION
La cellule suivante fournit deux tests pour la fonction pointsFigureYams
.
Dans la cellule qui la suit, écrivez aux moins deux autres 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 );
/// BEGIN SOLUTION
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 );
/// END SOLUTION
Compilation et tests en dehors de Jupyter#
Indication
Recommandation
Faites avec soin les questions de compilation pour vous y entraîner : cela vous servira pour la suite du semestre et notamment pour le projet qui sera entièrement sous forme compilée.
Ouvrez le fichier
yams.cpp
.Copiez-collez votre fonction
pointsFigureYams
à un endroit adapté.Insérez la ligne suivante à l’endroit indiqué dans la fonction
main
:cout << pointsFigureYams({1,1,1,1,1}) << endl;
Enregistrez votre fichier
yams.cpp
.Ouvrez un terminal
Compilez votre fichier
yams.cpp
en lançant, depuis le répertoireSemaine6
, la commande :clang++ yams.cpp -o yams
Attention
En salle de TP : pour avoir accès à
clang++
, nous vous recommadons d’utiliser le terminal intégré dans JupyterLab afin d’être dans le bon environnement logiciel.La commande doit être lancée dans le répertoire contenant
yams.cpp
. Si nécessaire, déplacez vous y avec la commandecd
(par exemplecd ~/ProgImperative/Semaine6
).Pour alterner efficacement entre ce document contenant les consignes, l’éditeur de fichier contenant
yams.cpp
et le terminal, nous vous recommandons de disposer les onglets correspondants de sorte qu’ils soient tous visibles simultanément (voir la feuille Jupyter de la semaine dernière.
Exécutez le programme ainsi généré à l’aide de la commande
./yams
. Votre terminal doit afficher 65.
Exercice 2 : Utilitaires#
Dans cet exercice, vous allez implanter plusieurs utilitaires qui pourront ensuite être combinés pour reconnaître les yams, les autres figures, et implanter le jeu lui-même.
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
}
Comme lanceDes
renvoie des valeurs aléatoires, c’est un peu plus
difficile de la tester. Exécutez à plusieurs reprises la cellule
suivante :
lanceDes()
On vérifie qu’elle renvoie toujours un tableau de longueur \(5\) :
CHECK( lanceDes().size() == 5 )
afficheDes
#
Pour afficher le contenu d’une variable, notamment dans un programme
compilé, vous avez maintenant l’habitude d’utiliser cout
. Cependant,
par défaut, cout
ne permet pas d’afficher un tableau. Vous pouvez
le constater en décommentant la deuxième ligne de la cellule ci-dessous
(enlever les //
en début de ligne) et en exécutant la cellule.
Remettez ensuite la deuxième ligne en commentaire et redémarrez le noyau.
vector<int> t = {5,-13,0,27};
// cout << t << endl;
Aussi, pour pouvoir afficher le contenu d’un tableau de dés, on va
écrire une fonction afficheDes
.
Complétez la documentation de la fonction afficheDes
puis définissez
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});
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 que votre fonction compteDes
est correcte à l’aide des
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 renvoie -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 que votre fonction chercheDansTableau
est correcte :
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
des
;afficher le résultat du tirage;
afficher le nombre de points obtenus en cherchant un yam’s dans ce tirage.
Testez votre chance en exécutant plusieurs fois la cellule!
vector<int> des;
/// BEGIN SOLUTION
des = lanceDes();
afficheDes(des);
cout << "Vous marquez " << pointsFigureYams(des) << " points." << endl;
/// END SOLUTION
Notes aux enseignants
Erreur classique : oublier de copier l’appel à srand
en début de
main()
; d’où comportement déterministe.
Compilation et tests en dehors de Jupyter#
Ouvrez votre fichier yams.cpp.
Copiez-collez les fonctions
afficheDes
,lanceDes
,compteDes
etchercheDansTableau
à un endroit adapté.Remplacez la ligne d’affichage contenue dans la fonction
main
par le code des deux cellules précédentes.Enregistrez votre fichier
yams.cpp
Compilez-le à l’aide de la commande
clang++ yams.cpp -o yams
Exécutez le programme ainsi généré :
./yams
. Le résultat est-il bien celui attendu ? Si votre programme tire toujours la même série de dés, que manque-t-il dans la fonctionmain
, qui était bien présent dans la première cellule de code, en haut de cette page ?
Exercice 3 : 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 compter 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 renvoyer 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 tests en dehors de Jupyter#
Ouvrez votre fichier
yams.cpp
.Copiez-collez les fonctions
aleaInt
,pointsFigureCarre
etpointsFigureFull
,pointsFigureBrelan
etpointsFigure
à un endroit adapté.Enregistrez votre fichier
yams.cpp
.Vérifiez qu’il compile toujours à l’aide de la commande
clang++ 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.
Exercice 4 : La boucle de jeu#
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 lancer à 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.
Complétez la fonction main
ci-dessous :
int main() {
// Initialisation de la fonction rand (pour les nombres aléatoires).
srand(time(0));
// AVANT LA BOUCLE PRINCIPALE
vector<int> des;
int score = 0;
string reponse = "";
// BOUCLE PRINCIPALE
while ( true ) {
// 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 lancer et on demande quelle figure y chercher.
/// 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 parmi
// "brelan", "carre", "full", "yams" et "exit".
do {
// L'instruction suivante attend que le joueur entre du texte au clavier
// et stocke sa réponse dans la chaine de caractères "reponse"
cin >> reponse;
} while ( reponse != "brelan"
and reponse != "full"
and reponse != "carre"
and reponse != "yams"
and reponse != "exit" );
// Si la réponse est "exit", on annonce la fin du jeu, on affiche le score et on termine.
if ( reponse == "exit" ) {
/// BEGIN SOLUTION
cout << "Fin du jeu, votre score final est de " << score << "points" << endl;
/// END SOLUTION
return 0;
}
// L'utilisateur a donné le nom d'une figure.
// On appelle **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
int points = pointsFigure(des, reponse);
score += points;
cout << "Vous marquez " << points << " points, votre score total est de " << score << " points." << endl;
/// END SOLUTION
}
// FIN DE LA BOUCLE PRINCIPALE
return 0;
}
puis testez-la :
// Cette incantation magique court-circuite l'appel à `main` -- qui
// requiert des interactions -- dans la correction automatique
#include <cstdlib>
if ( not getenv("NBGRADER_EXECUTION") )
main()
Compilation et test en dehors de Jupyter#
Complétez yams.cpp
afin de pouvoir jouer en l’exécutant
(après l’avoir compilé) depuis votre terminal.
Indication : Si jamais vous entrez dans une boucle infinie, vous pouvez stopper votre programme à tout moment en pressant les touches CtrlC
Exercice 5 : ♣ De la suite dans les idées#
Dans le jeux de Yam’s 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 !
Donnez 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,
* et sinon la somme des dés formant la grande suite + 50
**/
/// 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-la en exécutant les cellules dans le bon ordre.
Compilation et tests 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.
Tests#
Cette section vérifie que le programme yams
compile et s’exécute
sans erreur immédiate. Les commandes suivantes ne devraient pas
déclencher d’erreur.
CHECK( not system("clang++ yams.cpp -o yams") )
CHECK( not system("echo exit | ./yams") )