Bonjour j'ai un petit problème pour mon "jeu" du pendu. Il compile mais n'affiche pas les étoiles (*) et lorsque je rentre une lettre il me dit directement que j'ai gagné. Je pense donc que c'est un problème dans le choix du mot mais je n'arrive pas à voir où...
main.c :
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include "main.h"
#include "dico.h"
int main()
{
char motSecret[100] = {0};
int i = 0;
char maLettre = 0;
int nombreDeCoupsRestants = 10;
int *lettreTrouvee = NULL;
int tailleMot = 0;
FILE* dico = NULL;
dico = fopen("dico.txt", "r+");
if (dico != NULL)
{
if (!piocherMot(motSecret))
exit(0);
tailleMot = strlen(motSecret);
lettreTrouvee = malloc(tailleMot * sizeof(int));
if (lettreTrouvee == NULL)
exit(0);
do
{
printf("Il vous reste %d coup(s) a jouer.\n", nombreDeCoupsRestants);
for (i = 0 ; i < tailleMot ; i++)
{
if (lettreTrouvee[i] == 0)
printf("*");
else
printf("%c", motSecret[i]);
}
printf("\nPropose une lettre : ");
maLettre = lireCaractere();
if (!rechercheCaractere(maLettre, motSecret, lettreTrouvee))
nombreDeCoupsRestants--;
}while(nombreDeCoupsRestants != 0 && gagne(lettreTrouvee, tailleMot) == 0);
if (nombreDeCoupsRestants == 0)
printf("\nDesole tu as perdu, le mot qui etait a trouver etait %s\n\n", motSecret);
else
printf("\nBravo tu as bien trouve le mot : %s\n\n", motSecret);
}
else
{
printf("Impossible d'ouvrir le dictionnaire des mots\n\n");
}
return 0;
}
char lireCaractere()
{
char caractere = 0;
caractere = getchar();
caractere = toupper(caractere);
while (getchar() != '\n') ;
return caractere;
}
int rechercheCaractere(int lettre, char motSecret[], int notreMot[])
{
int i = 0;
int bonneLettre = 0;
for (i = 0 ; motSecret[i] != '\0' ; i++)
{
if (lettre == motSecret[i])
{
notreMot[i] = 1;
bonneLettre = 1;
}
}
return bonneLettre;
}
int gagne(int notreMot[], int tailleTableau)
{
int i = 0;
int gagne = 1;
for (i = 0 ; i < tailleTableau ; i++)
{
if (notreMot[i] == 0)
gagne = 0;
}
return gagne;
}
main.h :
#ifndef DEF_MAIN
#define DEF_MAIN
char lireCaractere();
int rechercheCaractere(int lettre, char motSecret[], int notreMot[]);
int gagne(int notreMot[], int tailleTableau);
#define DEBUT() printf("Bienvenue dans le jeu du pendu !\n"); \
printf("les regles sont simples, tu dois deviener\n"); \
printf("le mot cache, choisis par un autre utilisateur\n"); \
printf("ou par l'ordinateur. Pour simplifier, les accents\n"); \
printf("ne seront pas pris en compte et je vous demanderez \n"); \
printf("de bien entrer les lettres en MAJUSCULES !\n\n");
#endif
@Dwarliz elle fait bien ce qu'on attend d'elle C'est à dire nettoyer le tampon comme dirait les anglophobes
Merci, mon C est probablement plus rouillé que je ne veux l'admettre...
Sinon Nababe, bien que ça ne résout a priori pas ton problème, le srand (dans ton dico.c ligne 62), ne doit pas se faire avant chaque appel d'un nombre random, mais uniquement la 1ère fois. D'ailleurs, à le faire ainsi tu risques de te retrouver souvent avec le même nombre "random".
numMotChoisi = nombreAleatoire(nombreDeMot - 1); // dico.c -> ligne 30
/* Soutrait nombreDeMot de moins 1. */
fgets(notreMot, 100, dico); // dico.c -> ligne 40
/* motChoisi remplaçait par notreMot */
#Edit: Ajout d'un fix :
lettreTrouvee = calloc(tailleMot, sizeof(int)); // main.c -> ligne 35
/* Remplit la zone mémoire pointer par lettreTrouvee de 0 [ Plusieurs autres façons de faire ] */
Compile avec des options (-Wall au minimum, -Wextra devrait aussi être toujours utilisé, et d’autres sont aussi très utiles).
Quand une fonction ne prends pas de paramètre, utilise void. Ça vaut aussi pour main, surtout que int main() n’est pas une forme de main valide.
Dans le prototype de ta fonction nombreAleatoire, tu ne mets pas le type de nombreDeMots. De plus, je trouve bizarre qu’une fonction de ce nom prenne en paramètre un entier qui s’appelle nombreDeMots. Je te propose ça pour la remplacer.
/**
* \fn double random(void)
* \brief Tire au hasard un nombre entre 0 et 1.
* \return Un flottant entre 0 et 1.
*/
double random(void)
{
return (double) rand() / RAND_MAX;
}
/**
* \fn int randint(const int a, const int b)
* \brief Tire au hasard un entier entre a et b.
* \param a Borne inférieure.
* \param b Borne supérieure.
* \return Un entier entre a et b.
*/
int randint(const int a, const int b)
{
return (int)(a + (b - a) * random());
}
Si tu considères que le mot secret fera au maximum 99 caractères, autant faire pareil pourlettreTrouvee. Comme ça, tu supprimes une allocation dynamique. D’ailleurs, quand tu fais une allocation dynamique, vérifie qu’elle a réussi.
Si l’ouverture de ton fichier a échoué, utilise perror pour connaître la raison de l’échec.
Dans ta fonction lireCaractere, il se passe quoi si l’utilisateur n’entre pas une lettre. Je te propose celle-là à la place.
/**
* \fn void clearStdin(void)
* \brief Vide le buffer.
*/
void clearStdin(void)
{
int c;
while((c = getchar()) != EOF && c != '\n');
}
/**
* \fn char getLetter(void)
* \brief Demande à l’utilisateur une lettre de l’alphabet.
*
* \return Une lettre de l’alphabet.
*
* La fonction redemande à l’utilisateur de rentrer une lettre tant que sa saisie
* n’est pas valide.
*/
char getLetter(void)
{
char c = 0;
while(!scanf("%[a-zA-Z]c",&c))
{
clearStdin();
printf("Erreur, votre saisie est invalide. Recommencez :");
}
clearStdin();
return c;
}
Pour ta fonction piocherMot, ce serait beaucoup plus simple de lire tout le fichier et de stocker tous les mots dans un tableau de chaînes de caractères. Comme ça, tu ne lis le fichier qu’une seule fois (pour avoir une fonction rejouer c’est mieux), tu auras juste à tirer un nombre au hasard entre 0 et la taille de ton tableau - 1 et le mot secret sera tableau[hasard]. Pour faire cela, le mieux reste d’indiquer le nombre de mots du dictionnaire en premier ligne de ton fichier.
Fais plus de fonctions si nécessaire. En particulier, si tu gardes ta méthode pour choisir le mot, ce serait bien de découper ta fonction. Par exemple, tu aurais une fonction pour avoir le nombre de mots de ton fichier, une autre (que tu as déjà) pour tirer un nombre au hasard, etc. De même ton main pourrait être plus court. Voici un main que j’ai pour un pendu.
int main(void)
{
struct s_book *book = loadBook();
srand((unsigned int)(time(NULL)));
if(NULL != book)
{
do
{
const char *const word = getWord(book);
play(word);
}while(replay());
book = freeDico(book);
}
return 0;
}
EDIT : Ta fonction gagne peut être raccourcie. De plus, son premier paramètre est mal nommé, il ne s’agit pas d’un mot mais d’un tableau d’entiers.
Pour chaque élément du tableau d’entier Faire
Si cet élément est nul Alors
Renvoyer 0
Fin Si
Fin Pour
Renvoyer 1
Bon rebonjour, j'ai pas mal travaillé sur Le Pendu. Je suis de nouveau là pour demander quelque chose.
Là, le mot est choisis correctement, mais je pense que c'est dans la fonction gagne que ça pêche car il m'annonce que j'ai gagné dès la première lettre écrite, et ce même si elle est incorrect !
main.c
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include "main.h"
#include "dico.h"
int main()
{
int i = 0;
int *lettreTrouvee = NULL;
char motSecret[100] = {0};
int nombreDeCoupsRestants = 10;
char maLettre = 0;
int tailleDuMot = 0;
if(!piocherMot(motSecret))
exit(0);
tailleDuMot = strlen(motSecret);
lettreTrouvee = malloc(tailleDuMot * sizeof(int));
if (lettreTrouvee == NULL)
exit(0);
do
{
if(nombreDeCoupsRestants != 1)
printf("Il te reste %d coups a jouer\n", nombreDeCoupsRestants);
else
printf("Il te reste %d coup a jouer\n", nombreDeCoupsRestants);
for (i = 0 ; i < tailleDuMot ; i++)
{
if (lettreTrouvee[i] == 1)
printf("%c", motSecret[i]);
else
printf("*");
}
printf("\nPropose une lettre : ");
maLettre = lireCaractere();
if (!rechercheLettre(maLettre, motSecret, lettreTrouvee))
{
printf("Non ""%c"" n'est pas dans le mot\n\n", maLettre);
nombreDeCoupsRestants--;
}
else
printf("\n");
}while(nombreDeCoupsRestants != 0 && gagne(lettreTrouvee, tailleDuMot) == 0);
if (nombreDeCoupsRestants == 0)
printf("\n\n\nDesole tu as perdu, le mot qui etait a trouver etait %s\n\n", motSecret);
else
printf("\n\n\nBravo tu as bien trouve le mot : %s\n\n", motSecret);
return 0;
}
char lireCaractere()
{
char caractere = 0;
caractere = getchar();
caractere = toupper(caractere);
while (getchar() != '\n') ;
return caractere;
}
int gagne(int lettreTrouvee[], int tailleDuMot)
{
int i = 0;
for (i = 0 ; i < tailleDuMot ; i++)
{
if (lettreTrouvee[i] == 0)
return 0;
}
return 1;
}
int rechercheLettre(int lettre, char motSecret[], int lettreTrouvee[])
{
int i = 0;
int bonneLettre = 0;
for (i = 0 ; motSecret[i] != '\0' ; i++)
{
if (lettre == motSecret[i])
{
lettreTrouvee[i] = 1;
bonneLettre = 1;
}
}
return bonneLettre;
}
main.h
#ifndef DEF_MAIN
#define DEF_MAIN
char lireCaractere();
int gagne(int lettreTrouvee[], int tailleDuMot);
int rechercheLettre(int lettre, char motSecret[], int lettreTrouvee[]);
#define DEBUT() printf("Bienvenue dans le jeu du pendu !\n"); \
printf("les regles sont simples, tu dois deviener\n"); \
printf("le mot cache, choisis par un autre utilisateur\n"); \
printf("ou par l'ordinateur. Pour simplifier, les accents\n"); \
printf("ne seront pas pris en compte et je vous demanderez \n"); \
printf("de bien entrer les lettres en MAJUSCULES !\n\n");
#endif
dico.c
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include "dico.h"
int piocherMot(char *motPioche)
{
FILE* dico = NULL;
srand(time(NULL));
int numDuMot = 0;
int caractereLu = 0;
dico = fopen("dico.txt", "r+");
if (dico != NULL)
{
rewind(dico);
numDuMot = nombreAleatoire(compterNombreDeMot());
while (numDuMot > 0)
{
caractereLu = fgetc(dico);
if (caractereLu == '\n')
numDuMot--;
}
fgets(motPioche, 100, dico);
motPioche[strlen(motPioche) - 1] = '\0';
fclose(dico);
}
else
{
printf("\n\nLe dictionnaire de mot n'a pu etre charge\n\n");
return 0;
}
return 1;
}
int compterNombreDeMot()
{
char caractereActuel = 0;
int nombreDeMot = 0;
FILE* dico = NULL;
dico = fopen("dico.txt", "r+");
if (dico != NULL)
{
do
{
caractereActuel = fgetc(dico);
if (caractereActuel == '\n')
nombreDeMot++;
}while (caractereActuel != EOF);
fclose(dico);
}
else
{
printf("\n\nLe dictionnaire de mot n'a pu etre charge\n\n");
exit(0);
}
return nombreDeMot;
}
int nombreAleatoire(int nombreMax)
{
srand(time(NULL));
return (rand() % nombreMax);
}
dico.h
#ifndef DEF_MAIN
#define DEF_MAIN
int piocherMot(char *motPiocher);
int compterNombreDeMot();
int nombreAleatoire(int nombreMax);
#endif
Edit : J'ai trouvé ce qui allait pas. Encore merci pour tout
Je laisse ça ici, un code où le dictionnaire est chargé dans un tableau de chaînes de caractères.
/**
* \file main.c
* \brief Petit jeu du pendu
* \author Karnaj
*
* Jeu du pendu en langage C. Le programme choisit un mot au hasard que le
* joueur doit deviner en proposant des lettres (il a droit à 10 mauvaises
* lettres). Un fichier « dico.dic » doit être placé dans un dossier « rsc»
* à côté de l’exécutable. Celui-ci doit contenir un mot par ligne (chaque
* mot d’au plus 50 caractères), la première ligne contenant le nombre de
* mots du fichier.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
/**
* \def LEN_MAX Taille maximale d’un mot de la structure s_book.
*/
#define LEN_MAX 50
/**
* \def PATH Chemin du dictionaire à charger.
*/
#define PATH "rsc/dico.dic"
/**
* \struct s_book
* \brief Objet représentant un dictionnaire
*
* s_book est une structure contenant une liste de mots (de taille
* maximale `LEN_MAX`) et le nombre de mots de cette liste.
*/
struct s_book
{
unsigned int size;
char (*words)[LEN_MAX + 1];
};
struct s_book *freeDico(struct s_book *book);
FILE *loadFile(const char path[]);
struct s_book *loadBook(void);
int loadWords(struct s_book *const book, FILE *const file);
char *getWord(const struct s_book *const book);
void play(const char word[]);
int update(const char word[], char answer[], const char letter, const size_t len_word);
int replay(void);
double random(void);
int randint(const int a, const int b);
char getLetter(void);
void clearStdin(void);
/**
* \fn int main (void)
* \brief Entrée du programme. Lance le jeu du pendu
*
* \return 0 - Arrêt normal du programme.
*/
int main(void)
{
struct s_book *book = loadBook();
srand((unsigned int)(time(NULL)));
if(NULL != book)
{
do
{
const char *const word = getWord(book);
play(word);
}while(replay());
book = freeDico(book);
}
return 0;
}
/**
* \fn double random(void)
* \brief Tire au hasard un nombre entre 0 et 1.
* \return Un flottant entre 0 et 1.
*/
double random(void)
{
return (double) rand() / RAND_MAX;
}
/**
* \fn int randint(const int a, const int b)
* \brief Tire au hasard un entier entre a et b.
* \param a Borne inférieure.
* \param b Borne supérieure.
* \return Un entier entre a et b.
*/
int randint(const int a, const int b)
{
return (int)(a + (b - a) * random());
}
/**
* \fn int replay(void)
* \brief Demande à l’utilisateur s’il veut rejouer.
* \return Une valeur non nulle si l’utilisateur veut rejouer, 0 sinon.
*/
int replay(void)
{
int tmp = 0;
do
{
printf("\n\nVoulez vous recommencer ?\n"
"1. Continuer\n"
"2. Quitter\n");
scanf("%d", &tmp);
clearStdin();
}while(tmp != 1 && tmp != 2);
return tmp == 1;
}
/**
* \fn struct s_book *freeDico(struct s_book *book)
* \brief Libère une structure s_book allouée dynamiquement.
* \param book Un pointeur sur la structure s_book allouée dynamiquement.
* \return NULL.
*/
struct s_book *freeDico(struct s_book *book)
{
free(book->words);
free(book);
return NULL;
}
/**
* \fn int update(const char word[], char answer[], const char letter, const size_t len_word)
* \brief Met à jour le mot-réponse de l’utilisateur en fonction d’une lettre et vérifie que cette
* lettre est bien dans le mot référent.
* \param word Le mot référent, celui que l’utilisateur doit trouver.
* \param answer Le mot-réponse de l’utilisateur.
* \param letter La lettre a vérifier.
* \param len_word La longueur du mot à vérifier.
* \return NULL.
*/
int update(const char word[], char answer[], const char letter, const size_t len_word)
{
int success = 0;
size_t i = 0;
for(i = 0; i < len_word; i++)
{
if(letter == word[i])
{
answer[i] = letter;
success = 1;
}
}
return success;
}
/**
* \fn void play(const char word[])
* \brief Fonction de jeu du pendu.
* \param word Le mot à trouver.
*
* La fonction prend en paramètre un mot que l’utilisateur doit trouver. À chaque tour,
* il propose une nouvelle lettre jusqu’à avoir trouvé toutes les lettres du mot. Il a droit
* à 10 échecs.
*/
void play(const char word[])
{
char answer[LEN_MAX] = {0}, letter = 0;
size_t len_word = strlen(word), attempts = 10;
memset(answer, '*', len_word * sizeof(char)); /* Peut être remplacé par une boucle for */
while(attempts > 0 && strcmp(answer, word))
{
printf("\nIl vous reste %u essai%s\nLe mot est %s.\nEntrez une lettre : ",
attempts, attempts == 1 ? "." : "s.", answer);
letter = (char) toupper(getLetter());
if(update(word, answer, letter, len_word))
printf("Bravo, %c est bien dans le mot.\n", letter);
else
attempts --;
}
if(attempts > 0)
printf("Bravo, vous avez gagne en %u essai%s, le mot etait bien %s.\n",
10 - attempts, attempts == 10 ? "." : "s.", word);
else
printf("Vous avez perdu, looser. Le mot etait %s.", word);
}
/**
* \fn void clearStdin(void)
* \brief Vide le buffer.
*/
void clearStdin(void)
{
int c;
while((c = getchar()) != EOF && c != '\n');
}
/**
* \fn char getLetter(void)
* \brief Demande à l’utilisateur une lettre de l’alphabet.
*
* \return Une lettre de l’alphabet.
*
* La fonction redemande à l’utilisateur de rentrer une lettre tant que sa saisie
* n’est pas valide.
*/
char getLetter(void)
{
char c = 0;
while(!scanf("%[a-zA-Z]c",&c))
{
clearStdin();
printf("Erreur, votre saisie est invalide. Recommencez :");
}
clearStdin();
return c;
}
/**
* \fn FILE *loadFile(const char path[])
* \brief Ouvre le fichier dont le chemin est path.
* \param path Chemin du fichier à ouvrir.
* \return Un pointeur sur FILE correspondant au fichier ouvert (NULL en cas d’erreur).
*/
FILE *loadFile(const char path[])
{
FILE *file = fopen(path, "r");
if(NULL == file)
perror("Erreur chargement file dictionnaire ");
return file;
}
/**
* \fn struct s_book *loadBook(void)
* \brief Charge un dictionnaire depuis le fichier dont le chemin est PATH.
* \return Un pointeur sur la structure s_book créée (NULL en cas d’erreur).
*/
struct s_book *loadBook(void)
{
struct s_book *book = malloc(sizeof(book));
if(NULL == book)
perror("Erreur malloc struct s_book ");
else
{
FILE *file = loadFile(PATH);
book->words = NULL;
if(NULL == file)
book = freeDico(book);
else
{
if(loadWords(book, file) < 0)
book = freeDico(book);
fclose(file);
}
}
return book;
}
/**
* \fn int loadWords(struct s_book *const book, FILE *const file)
* \brief Charge les mots d’un fichier dans une structure s_book.
* \param book Pointeur sur la structure s_book dans laquelle les mots doivent être chargés.
* \param file Correspond au fichier contenant les mots.
* \return 0 en cas de succès et -1 en cas d’erreur.
*
* La fonction suppose que le fichier est formaté de la manière suivante :
*
* - le nombre de mots en première ligner du fichier ;
* - puis un mot par ligne.
*/
int loadWords(struct s_book *const book, FILE *const file)
{
size_t i = 0;
if(fscanf(file, "%u%*[^\n]", &book->size) == 0)
{
printf("Le fichier dictionnaire est corrompu.\n");
return -1;
}
book->words = malloc(book->size * sizeof(*book->words));
if(book->words == NULL)
{
perror("Erreur malloc ");
return -1;
}
for(i = 0; i < book->size; i++)
if(fscanf(file, "%50s%*[^\n]", book->words[i]) == 0)
{
printf("Le fichier dictionnaire est corrompu.\n");
return -1;
}
return 0;
}
/**
* \fn char *getWord(const struct s_book *const book)
* \brief Tire un mot au hasrd dans un objet s_book.
* \param book Pointeur sur la structure s_book dans laquelle tirer le mot.
* \return Le mot tiré.
*/
char *getWord(const struct s_book *const book)
{
int i = randint(0, (int)book->size - 1);
return book->words[i];
}
</ctype.h></string.h></time.h></stdlib.h></stdio.h>
.., mais je pense que c'est dans la fonction gagne que ça pêche car il m'annonce que j'ai gagné dès la première lettre écrite, et ce même si elle est incorrect !
Loin de là , c'est dù à l'allocation dynamique ligne:29, rien ne te garantie que le contenu dans la zone mémoire allouée est remplit de zero : ( ref: mon précédent message ! )
ASW_ a écrit:
Ajout d'un fix :
lettreTrouvee = calloc(tailleMot, sizeof(int)); // main.c ligne:29
/* Remplit la zone mémoire pointer par lettreTrouvee de 0 [ Plusieurs autres façons de faire ] */
Quand je dis que j'ai trouvé, j'ai oublié d'indiquer l'erreur que j'ai corriger. Ne connaissant pas "calloc" j'ai ré-utilisé malloc en utilisant une boucle pour fixer la zone mémoire avec des 0. Mais j'en déduis donc que l'utilisation de calloc serait plus optimisé, merci.
Quant à
ASW_ a écrit:
Non, il ne l'est que partiellement, par exemple si tu tombes sur nombreMax lors du tirage au hasard du nombre.
Au risque de me répéter comme tu n'as pas l'air d'avoir pris compte de mon précédent message voici une des solutions pour palier à ce problème :
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Are you watching closely?
Are you watching closely?