Problème de logique d'une boucle dans une boucle [Résolu]

madhack 24 Messages postés lundi 28 novembre 2016Date d'inscription 27 avril 2017 Dernière intervention - 21 févr. 2017 à 00:14 - Dernière réponse : cptpingu 3794 Messages postés dimanche 12 décembre 2004Date d'inscriptionModérateurStatut 10 juin 2018 Dernière intervention
- 24 févr. 2017 à 13:12
Bonjour,
Je suis entrain de faire le tp mot mystère d'un cour que je suis sur le c++.
Il viens du siteduzero.
Je vous présente le code
#include <iostream>
#include <string>

int main()
{
std::cout << "Veuillez donner un mot : " << std::endl;
std::string motUser,motJoueur;
std::cin>>motUser;
int essaie(5);
char choixUser;
//do
//{
if(essaie>0)
{
do
{
std::cout<<"Quel est ce mot ?"<<std::endl;
std::cin>>motJoueur;
if(motUser==motJoueur)
{
std::cout<<"Bravo"<<std::endl;
std::cout<<"Voulez vous rejouer une partie ? (y/n)"<<std::endl;
std::cin>>choixUser;
}
else (essaie>0)
{
essaie-=1;
std::cout<<"Ce n'est pas le mot il vous reste : "<<essaie<<" essaies."<<std::endl;
}

}while(motUser!=motJoueur&&essaie>0);
}
else
{
std::cout<<"PERDU le mot est : "<<motUser<<std::endl;
std::cout<<"Voulez vous rejouer une partie ? (y/n)"<<std::endl;
std::cin>>choixUser;
}
//}while(choixUser!='n');
return 0;
}

Mon objectif est de demander à l'utilisateur de deviner le mot.
Il à 5 essaies pour le faire au bout des 5 essaies la partie est perdu et l'on affiche le mot.
Mon bout de programme à l'heure actuelle fonctionne partiellement.
Si la personne trouve la bonne réponse avant les 5 essaies. Le bon message s'affiche
SI la personne use les 5 essaies la boucle s’exécute 5 fois avec le décompte afficher comme voulu.
Seulement une fois le dernier essaie effectuer le programme s’arrête sans afficher le message PERDU.
Et je bute depuis plusieurs heures sur le problème au point de me dire que je ne suis peut être pas en mesure d'apprendre un langage de programmation.
Je suis sur que la solution est toute simple et je passe a cote depuis plusieurs jours.
Si une personne bienveillante voulais bien me donner une direction une lumière ou m'achever en me disant de plutôt faire du tricot.
Merci
Afficher la suite 

Votre réponse

11 réponses

cptpingu 3794 Messages postés dimanche 12 décembre 2004Date d'inscriptionModérateurStatut 10 juin 2018 Dernière intervention - Modifié par cptpingu le 21/02/2017 à 15:16
0
Merci
Bonjour.

Quelque soit le niveau que tu as, il est normal de buter à un moment ou à un autre. Ça t'arrivera encore, et même après plusieurs années de pratique, tu n'y échapperas pas :). C'est ce que l'on appelle "l'effet tunnel". Quand on passe trop de temps sur un problème, on ne voit plus qu'un type de résolution, sans voir les autres autour. C'est là qu'un oeil extérieur est utile. En entreprise on fait ce que l'on appelle des "code review" (relecture de code par autrui), justement pour éviter cela. Donc, pas de panique, c'est tout à fait normal.

Pour en revenir au sujet. La logique pour les 5 essais est la bonne, mais pas celle pour vérifier la "défaite". Si tu entres dans ton premier if, quoi que tu fasses à l'intérieur, tu n'iras jamais dans le "else" associé. C'est donc normal que tu ne puisses pas atteindre le "perdu".
Pour que tu visualises:
if (condition)
{
// Logique du while <= Si tu arrives ici, tu ne peux pas arriver dans le else qui est un "étage" plus haut
}
else
// Perdu


Pour t'en sortir, il faut couper ta logique en deux. Une partie pour relancer, et une partie pour faire chercher le mot. Théoriquement, il serait particulièrement adapté d'utiliser des fonctions, mais comme je ne suis pas sur que tu sois déjà à l'aise avec, on va faire sans.

La premiere partie doit dire quelque chose comme:
stop = false;
while (!stop)
{
// code métier
"Voulez-vous rejouez (y/n)"
Si 'n', stop = true;
}


Le "code métier" lui, dit:
"choisir mot à devenir"

tant que (essaie > 5)
{
"Demander mot au joueur"
Si identique, alors "bravo", fin de la boucle (avec le mot clé "break").
Sinon, --essaie, "mauvais mot".
}

En sortie de boucle, si essaie == 0, c'est qu'on a perdu.
(si on est sortie avant, alors essaie est forcément > 0, et donc on ne fait rien car on a déja du afficher un "bravo".)


Le tout au propre, ça donnera:
#include <iostream>
#include <string>

int main()
{
  bool stop = false;

  while (!stop)
  {
    std::cout << "Veuillez donner un mot : " << std::endl;
    std::string motUser;
    std::cin >> motUser;

    int essaie = 5;
    while (essaie > 0)
    {
      std::string motJoueur;
      std::cout << "Quel est ce mot ?" << std::endl;
      std::cin >> motJoueur;
      if (motUser == motJoueur)
      {
         std::cout << "Bravo" << std::endl;
         break;
      }
      else
      {
         --essaie;
         std::cout << "Ce n'est pas le mot il vous reste : " << essaie << " essaies." << std::endl;
      }
    }

    if (essaie == 0)
      std::cout << "PERDU le mot etait : " << motUser << std::endl;

    std::cout << "Voulez vous rejouer une partie ? (y/n)" << std::endl;
    char choixUser;
    std::cin >> choixUser;
    if (choixUser == 'n')
      stop = true;
  }

  return 0;
}


Quelques conseils supplémentaires:
  • Déclare une variable au moment où tu l'utilises. En C++, on ne fait pas de pré-déclaration (c'était une vieille limitation du C).
  • Enchaine tes std::cout, au lieu d'en faire plein. Ex:
    std::cout << "toto" << std::endl
              << "titi" << std::endl; // pas besoin de remettre std::cout 
  • Il est possible de se passer du mot clé break, en ajoutant dans la condition "essaie > 0 && motJoueur != motUtilisateur"


Améliorer votre expérience CodeS-SourceS avec ce plugin:
http://codes-sources.commentcamarche.net/forum/affich-10000111-plugin-better-cs-2#cptpingu-signature
Commenter la réponse de cptpingu
madhack 24 Messages postés lundi 28 novembre 2016Date d'inscription 27 avril 2017 Dernière intervention - Modifié par madhack le 21/02/2017 à 22:22
0
Merci
Ah merci beaucoup.
Je vois que vous avez intégré la possibilité de rejouer dans votre correction.
J'ai suivi vos conseil hormis la var boll stop que je n'ai pas integré mais fait une boucle
do 
while(choixUser!='n')

j’espère rapidement maîtriser cette logique.
Maintenant le prochaine étape créer une version 1 ou 2 joueurs.
If 1 player alors chercher avec un ifstream un mot au hasard dans un fichier dictionnaire.
else 2 joueurs alors même système.
et le bouquet final un score par partie (valeur de la variable essaie = nombres de points)
stocker dans un vector pour faire une moyenne sur toute les parties jouer dans la session.
Commenter la réponse de madhack
cptpingu 3794 Messages postés dimanche 12 décembre 2004Date d'inscriptionModérateurStatut 10 juin 2018 Dernière intervention - Modifié par cptpingu le 22/02/2017 à 11:07
0
Merci
Maintenant le prochaine étape créer une version 1 ou 2 joueurs.

En prochaine étape, je mettrais aussi la maîtrise de la découpe du code en petites fonctions. Pour un petit code ce n'est pas gênant, mais là, ça va commencer à être dégueux si tu ne fais pas ça avec un code qui grossit :).

If 1 player alors chercher avec un ifstream un mot au hasard dans un fichier dictionnaire.

Je ferais une fonction qui prend un nom de fichier, et qui remplit un std::vector<std::string>. On mélange le vector via std::random_shuffle, et on prend ensuite les mots dans l'ordre.

et le bouquet final un score par partie (valeur de la variable essaie = nombres de points) stocker dans un vector pour faire une moyenne sur toute les parties jouer dans la session.

Je mettrais les scores dans un fichier.

En idée intéressante supplémentaire (et un peu plus "utile/fun" que ce que tu proposes), lorsqu'un mot est proposé et n'est pas le bon, on conserve les lettres qui sont bonnes (en précisant, si elles sont à la bonne position ou non). Un peu comme un "mastermind" ou un "motus". On affichera au préalable le nombre de lettres sous la forme "_ _ _ _ _".

exemple:

// Le mot à chercher est "voiture"
// <> représente une bonne lettre mal placée
// [] représente une bonne lettre bien placée

_ _ _ _ _ _ _

=> anxieux

anx<i><e><u>x
_ _ _ _ _ _ _

=> vaccine

[v]acc<i>n[e]
v _ _ _ _ _ e

=> veinure

[v]<e>[i]n[u][r][e]
v _ i _ u r e

=> voiture

[v][o][i][t][u][r][e]
v o i t u r e

=> Bravo!


Voici un dictionnaire de mot directement utilisable:
http://www.pallier.org/ressources/dicofr/liste.de.mots.francais.frgut.txt


Améliorer votre expérience CodeS-SourceS avec ce plugin:
http://codes-sources.commentcamarche.net/forum/affich-10000111-plugin-better-cs-2#cptpingu-signature
Commenter la réponse de cptpingu
madhack 24 Messages postés lundi 28 novembre 2016Date d'inscription 27 avril 2017 Dernière intervention - 22 févr. 2017 à 11:27
0
Merci
Bonjour merci pour les idées.
En ce qui concerne la découpe du code j'ai appris à faire
le main.cpp
la fonction dans nomFonction.cpp
et le header dans nomFonction.hh avec le prototype
mais je ne sais pas comment découper en plusieurs fonctions
puis-je mettre toutes les fonctions dans le fichierFonction.cpp
et reporter tous les prototypes dans le header
ou dois je faire plusieurs fichiers avec plusieurs header.
je vais me contenter de mettre les fonctions au dessus du main pour commencer.
Commenter la réponse de madhack
cptpingu 3794 Messages postés dimanche 12 décembre 2004Date d'inscriptionModérateurStatut 10 juin 2018 Dernière intervention - Modifié par cptpingu le 22/02/2017 à 12:04
0
Merci
Ne pas confondre: découpe en petites fonctions, et découpe en fichiers. Les deux ne sont pas liés (même si naturellement, tu vas vite découper ton fichier main.cpp en plusieurs fichiers).

Si on découpe ton code, ça donnera:
#include <iostream>
#include <string>

namespace
{
  void searchWord(const std::string& motUser)
  {
    std::string motJoueur;
    int essaie = 5;
    while (essaie > 0 && motUser != motJoueur)
    {
      std::cout << "Quel est ce mot ?" << std::endl;
      std::cin >> motJoueur;
      if (motUser != motJoueur)
      {
        --essaie;
        std::cout << "Ce n'est pas le mot il vous reste : " << essaie << " essaies." << std::endl;
      }
    }

    if (essaie == 0)
      std::cout << "PERDU le mot etait : " << motUser << std::endl;
    else
      std::cout << "Bravo" << std::endl;
  }

  void play()
  {
    char choixUser;
    do
    {
      std::cout << "Veuillez donner un mot : " << std::endl;
      std::string motUser;
      std::cin >> motUser;

      searchWord(motUser);

      std::cout << "Voulez vous rejouer une partie ? (y/n)" << std::endl;
      std::cin >> choixUser;
    } while (choixUser == 'y');
  }
}

int main()
{
  play();

  return 0;
}


Le mot "namespace" veut dire ici: namespace anonyme. Ça se traduit par: toutes les fonctions à l'interieur de cette zone sont locales au fichier et ne peuvent être utilisées dans un autre fichier. Si tu n'es pas à l'aise avec ce mécanisme, tu peux le retirer.

Améliorer votre expérience CodeS-SourceS avec ce plugin:
http://codes-sources.commentcamarche.net/forum/affich-10000111-plugin-better-cs-2#cptpingu-signature
Commenter la réponse de cptpingu
madhack 24 Messages postés lundi 28 novembre 2016Date d'inscription 27 avril 2017 Dernière intervention - 22 févr. 2017 à 23:11
0
Merci
hum très intéressant.
Je suppose que les headers et autres cpp liée a celui ci sont pour des fonctions plus évoluer et vouez à être réutilisé.
Je tente de faire une version simple du jeu avec un fichier txt comme base de donné

avec le code qui suit
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include <fstream>
//FAIRE TESTE GETLINE+IGNOREGETLINE

int main()
{
srand(time(0));
std::string motMystere,motDico;
std::ifstream fluxDico("C:/ExerciceC++motMystereLaurentK/dico.txt");
if(fluxDico)
{
//on verifie que le fichier est bien ouvert
int nbMotDico;
while(getline(fluxDico,motDico))//boucle calculant le nombre de mot de la base de donnée.
{
nbMotDico++;
}
int randMotDico;
randMotDico=rand()%nbMotDico;
fluxDico.seekg(0,std::ios::beg);//se replacer en debut de fichier
for (int i(0);i<randMotDico;i++)
{
getline(fluxDico,motMystere);
std::cout<<"Voici le mot : "<<motMystere<<" nombre de mot : "<<nbMotDico<<std::endl;
}
std::cout<<"numéro rand : "<<randMotDico<<std::endl;
}
else
{
std::cout<<"Le fichier ne c'est pas ouvert veuillez vérifier le chemin d'accès svp et le changer si necessaires"<<std::endl;
}

}

Je vois bien les cout entre " " mais pas le mot
j'ai essayer avec la methode mot à mot
fluxdico>>motMystere

même resultat
Commenter la réponse de madhack
cptpingu 3794 Messages postés dimanche 12 décembre 2004Date d'inscriptionModérateurStatut 10 juin 2018 Dernière intervention - Modifié par cptpingu le 23/02/2017 à 12:22
0
Merci
Tu peux, certes, compter le nombre de lignes pour prendre une ligne au hasard dans le fichier, mais ce n'est pas très beau dans le sens où:
  • Tu vas lire deux fois le fichier (même si la deuxième fois on lira "moins").
  • Si tu veux "rejouer une partie", tu vas entièrement relire ce fichier à nouveau (et deux fois).


Soit dit en passant, tu utilises du srand + rand qui est pratique pour du C ou du vieux C++, mais ça fait 7-8 ans qu'on a mieux :) (Oui ton bouquin n'est pas terrible sur certains points notamment sur celui-ci). Il y a le header <random>, qui donne plusieurs types de random mieux foutu, voir: http://www.cplusplus.com/reference/random et ici pour un exemple: http://stackoverflow.com/questions/19665818/generate-random-numbers-using-c11-random-library.
Retiens juste ceci: si tu utilises du rand/srand en C++, ce n'est pas terrible :).

Il y a une méthode plus simple ne demandant pas d'utiliser explicitement de random (ça sera fait en interne). Tu mélanges avec std::shuffle (c++11) ou std::random_shuffle (vieux C++03). Ce que je te propose c'est de lire le fichier dans un vecteur, de le mélanger, puis de lire celui-ci qui sera commun à toutes les parties.

Exemple (de tête, non testé):
#include <fstream>
#include <iostream>
#include <algorithm>    // Pour std::shuffle et std::random_shuffle
#include <random>       // Pour std::default_random_engine (C++11 seulement)
#include <chrono>       // Pour std::chrono::system_clock (C++11 seulement)

bool loadDictionnary(const std::string& filename, std::vector<std::string>& dico)
{
  std::ifstream file(filename.c_str());
  if (!file)
    return false;

  std::string word;
  while (std::getline(file, word))
    dico.push_back(word);

  // Si C++03 (bof ! Utilise du srand/rand en interne):
  std::random_shuffle(dico.begin(), dico.end());

  // Si C++ moderne (mieux !):
  // unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
  // std::shuffle(dico.begin(), dico.end(), std::default_random_engine(seed));

  return true;
}

std::string nextWord(const std::vector<std::string>& dico, unsigned int& index)
{
  std::string res = dico[index % dico.size()];
  ++index;
  return res;
    // On pourrait juste faire dico[index],
    // mais si jamais index >= dico.size, on sera hors borne. Ce qui n'arrivera jamais avec le modulo.
}

int main()
{
  std::vector<std::string> dico;
  if (!loadDictionnary("dico.txt", dico))
  {
    std::cerr << "Can't load dico.txt!" << std::endl;
    return 1;
  }

  unsigned int index = 0;

  //do
  //{
      // code...
      std::string motUser = nextWord(dico, index); // A chaque fois qu'on appelle nextWord(), on aura un nouveau mot au hasard.
      // searchWord(motUser);
      // code...
  //} while etc...

  return 0;
}



Améliorer votre expérience CodeS-SourceS avec ce plugin:
http://codes-sources.commentcamarche.net/forum/affich-10000111-plugin-better-cs-2#cptpingu-signature
Commenter la réponse de cptpingu
madhack 24 Messages postés lundi 28 novembre 2016Date d'inscription 27 avril 2017 Dernière intervention - 23 févr. 2017 à 14:59
0
Merci
merci pour ce développement.
Mais la question est le fait que mon getline ne m'affiche rien dans la console.Ce qui ne devrais pas se passer de la sorte.
Commenter la réponse de madhack
cptpingu 3794 Messages postés dimanche 12 décembre 2004Date d'inscriptionModérateurStatut 10 juin 2018 Dernière intervention - Modifié par cptpingu le 23/02/2017 à 15:29
0
Merci
Plusieurs soucis dans ton code:
  • Tu fais un .seekg() pour revenir au début fichier, mais c'est trop tard, la fin de fichier a été atteinte et un "flag" a été déposé pour dire que le fichier a déjà été lu. Donc un seekg ne suffit pas, il faut aussi réinitialiser ce "flag". Il te faut donc faire: "fluxDico.clear();" suivi de "fluxDico.seekg(0, std::ios::beg);" pour que ça fonctionne. D'une manière générale, on évite de se déplacer comme cela dans un fichier. C'est mieux de charger celui-ci dans une structure en mémoire (comme un std::vector).
  • nbMotDico n'est pas initialisé ! Déclarer une variable ne veut pas dire que celle-ci vaudra 0 par défaut. nbMotDico aura une valeur indéfinie. Ton nombre de lignes dans le fichier pourra très bien fonctionner dans 99% des cas, et de temps en temps faire n'importe quoi... Correction: "int nbMotDico = 0;"
  • Enfin, ce n'est pas une erreur technique, plutôt fonctionnelle, mais tu ne donnes pas un mot au hasard, mais un nombre de mots au hasard.


Améliorer votre expérience CodeS-SourceS avec ce plugin:
http://codes-sources.commentcamarche.net/forum/affich-10000111-plugin-better-cs-2#cptpingu-signature
Commenter la réponse de cptpingu
madhack 24 Messages postés lundi 28 novembre 2016Date d'inscription 27 avril 2017 Dernière intervention - Modifié par madhack le 23/02/2017 à 22:13
0
Merci
J'ai tester mon code complet donne :

#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include <fstream>

std::string melangeLettres(std::string mot)
{
int position;
std::string motRecu;
while(mot.size()!=0)
{
position=rand()%mot.size();
motRecu+=mot[position];
mot.erase(position,1);
}
return motRecu;
}

int main()
{
srand(time(0));
std::string motMystere,motMelange,motUser,motDico;
std::ifstream fluxDico("C:\ExerciceC++motMystereLaurentK\dico");
if(fluxDico)
{
//on verifie que le fichier est bien ouvert
int nbMotDico=0;
while(getline(fluxDico,motDico))//boucle calculant le nombre de mot de la base de donnée.
{
nbMotDico++;
}
int randMotDico;
randMotDico=rand()%nbMotDico;
fluxDico.clear();//suppression du flag
fluxDico.seekg(0,std::ios::beg);//remise au debut du curseur
for (int i(0);i<randMotDico;i++)
{
getline(fluxDico,motMystere);
}
}
else
{
std::cout<<"Le fichier ne c'est pas ouvert veuillez vérifier le chemin d'accès svp et le changer si necessaires"<<std::endl;
}
//tester avec fluxDico>>mot mais le mot à chercher reste désespérement vide.
for(int i(0);i<45;i++)
{
std::cout<<std::endl;
}
motMelange=melangeLettres(motMystere);
char choisUser;
do
{
int essaies(5);
while(essaies>0)
{
std::cout<<"Quel est le mot : "<<motMelange<<std::endl;
std::cin>>motUser;
if(motUser==motMystere)
{
std::cout<<"Bravo"<<std::endl<<"Voules vous refaire une partie ? (y/n)"<<std::endl;
std::cin>>choisUser;
break;
}
else
{
--essaies;
std::cout<<"Ce n'est pas le mot il vous reste : "<<essaies<<" essaies"<<std::endl;
}
}
if(essaies==0)
{
std::cout<<"PERDU le mot est : "<<motMystere<<std::endl<<"Voules vous refaire une partie ? (y/n)"<<std::endl;
std::cin>>choisUser;
}
}while(choisUser!='n');
return 0;
}

et voici le lien pour le dico
http://www.siteduzero.com/uploads/fr/ftp/mateo21/cpp/dico.zip

mais toujours pas de mot dans ma console juste un champs vide
Bah je vais rendre l'exercice comme tel et expliquer mon problème.
Merci beaucoup pour cette aide et tout le temps passé.
Commenter la réponse de madhack
cptpingu 3794 Messages postés dimanche 12 décembre 2004Date d'inscriptionModérateurStatut 10 juin 2018 Dernière intervention - Modifié par cptpingu le 24/02/2017 à 13:12
0
Merci
Si vraiment ton fichier ne se lit pas, alors en méthode "sale", il suffit de fermer et de réouvrir ton fichier. J'insiste sur le fait que ce n'est pas la meilleure méthode et que c'est crade :).

    std::ifstream fluxDico("C:\ExerciceC++motMystereLaurentK\dico");
    if(fluxDico)
    {
        //on verifie que le fichier est bien ouvert
        int nbMotDico=0;
        while(getline(fluxDico,motDico))//boucle calculant le nombre de mot de la base de donnée.
        {
            nbMotDico++;
        }
        int randMotDico;
        randMotDico=rand()%nbMotDico;
        fluxDico.close();
        fluxDico.open("C:\ExerciceC++motMystereLaurentK\dico");
        for (int i(0);i<randMotDico;i++)
                {
                    getline(fluxDico,motMystere);
                }
    }
    else
    {
        std::cout<<"Le fichier ne c'est pas ouvert veuillez vérifier le chemin d'accès svp et le changer si necessaires"<<std::endl;
    }


Améliorer votre expérience CodeS-SourceS avec ce plugin:
http://codes-sources.commentcamarche.net/forum/affich-10000111-plugin-better-cs-2#cptpingu-signature
Commenter la réponse de cptpingu

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.