Contenu | Rechercher | Menus

Annonce

Si vous rencontrez des soucis à rester connecté sur le forum (ou si vous avez perdu votre mot de passe) déconnectez-vous et reconnectez-vous depuis cette page, en cochant la case "Me connecter automatiquement lors de mes prochaines visites".
Test de l'ISO d'Ubuntu francophone : nous avons besoin de testeurs pour la version francophone d'Ubuntu 14.04. Liens et informations ici.

Attention, une faille de sécurité dans bash a récemment été rapportée, il est recommandé de mettre à jour son système (plus de détails) *** mise à jour 12/10/2014 ***

#1 Le 20/01/2006, à 19:04

matche

programmation C : aide et conseils

Bonjour,
Je suis un cours de C, et dans le cadre 'un TP, je dois faire un petit "jeu" dont voici les regles :
2 joueurs, le premier choisit un nombre entre 1 et 1000, le second doit le deviner en un minimum de coups.
TP 1: on joue contre l'ordi.
TP 2: les 2 processus jouent l'un contre l'autre.

TP 1:
L'ordi (= serveur) choisit un nombre entre 1 et 1000.
Le joueur propose au serveur des nombres et le serveur répond par "trop grand" ou "trop petit" ou" trouvé". Le serveur comptabilise le nombre de coups joués et l'affichera lorsque le joueur aura trouvé le nombre secret.
Le joueur ne disposera que de 10 secondes pour proposer un nouveau nombre, sinon un coup joué sera comptabilisé.
Le processus père se charge de faire jouer l'utilisateur (saisie d'un nombre, envoi au serveur...)
Le processus se charge du rôle du serveur (choix du nombre secret...) Père et fils utilisent un tube de communication.

TP 2 : le père joue contre le fils le fils n'affiche plus d'informations, mais donne les indications par l'envoi de signaux: plus grand= SIGUSR1; plus petit= SIGUSR2; égalité = SIGINT.
Comportement du père: il propose un premier nombre dans l'intervalle de recherche (Binf, Bsup)valant respectivement 1 et 1000 au départ; en fonction des signaux reçus SIGUSR1 et SIGUSR2, il réduit l'intervalle de recherche et propose un nombre dans ce nouvel intervalle;à la réception de SIGINT, il affiche le nombre de coups joués et se termine.
on connait la fonction hasard (cf TP5) pour obtenir un nombre aléatoire.

Je vous met ce que j'ai fait ci dessous avec lequel j'ai 2 problèmes sur le TP 1 :
1/ Lorsque je rentre une lettre, le "jeu" plante royalement : le texte défile !
2/ A la fin du jeu, le serveur ne rends pas la main.

Auriez vous une solution ? Si possible bien expliquée que je comprenne, ceci étant le but du TP !

Je vous mets aussi en dessous le code du 2eme TP qui fonctionne, si vous pouvez y jeter un coup d'oeil et me donner vos impressions, erreur de programation, ou idées pour l'améliorer,...



TP 1 :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>


/* Objets utilisés par le père et le fils */
  int tube[2];
  int cpt=0; /* c'est le compteur de nombre de coups joués */
  int nbmin = 0;
  int nbmax = 1000;

void gereFichier() {
  int tab[4] = {cpt, cpt, cpt, 1};
  FILE *fp = fopen("scores.txt", "r+b");

  if (fp != NULL) {
     fread(tab, sizeof(int), 4, fp);
     if (cpt < tab[0]) tab[0] = cpt;
     if (cpt > tab[1]) tab[1] = cpt;
     tab[2] = tab[2] + cpt;
     tab[3] = tab[3] + 1;
     fclose(fp);

}

fp = fopen("scores.txt", "wb");
fwrite(tab, sizeof(int), 4, fp);

fprintf(stderr, "\n\nBravo, c'est le bon chiffre, tu as trouve en %d coups.\n", cpt);

if (cpt == tab[0]) printf("Felicitation, tu viens de pulveriser le score !!!\n");
else if (cpt == tab[1]) printf("Pffff, jamais eu de score aussi minable !!!\n");
else if (cpt >= ((tab[2]/tab[3])-2) & (cpt <= (tab[2]/tab[3])+2)) printf("Dans la moyenne...\n");
else printf("Un score parmis tant d'autres, rien d'exceptionnel...\n");
printf("\nStatistiques :\n",tab[0]);
printf("Le nombre de couts minimum est %d\n",tab[0]);
printf("Le nombre de couts maximum est %d\n",tab[1]);
printf("Le nombre de couts moyen est %d\n",tab[2] / tab[3]);

// on ferme le fichier
fclose(fp);
exit(0);
}


void error (char *msg) {
  fprintf(stderr,"%s",msg);
  exit(-1);
}

int hasard(int bInf, int bSup) {
  return((rand() % (bSup+1-bInf)) + bInf);

}

int Compare(int a, int b) {
  if (a<0) {
    return -1; }
  else if (a<b) {
       return 0;}
  else if(a>b) {
       return 1;}
   else {
       return 2;}
}

void alarm_handler(int nb){
  printf("\nL'heure tourne, plus vite l'escargot ! Vous avez un coup en moins.\n");
  printf ("\nVeuillez entrer un nombre entre 0 et 1000: ");
  cpt++;
  alarm(10);
}

void mauvaisFormat(){
  printf("\nDésolé, il faut savoir taper sur un clavier pour jouer. Vous avez un coup en moins.\n");
  printf ("\nVeuillez entrer un nombre entre 0 et 1000: ");
  cpt++;
  alarm(10);
}

void bodyPere() {
  //signal(SIGINT, SIGINT);
  int nb;
  close(tube[0]); /* Le père ne lira jamais dans le tube */
  while(1) {
    signal(SIGALRM,SIG_IGN);
    scanf ("%d",&nb); /* saisie du nombre */
        write(tube[1], &nb, sizeof(int));
    }
}

 
void bodyFils() {
  int nc;/* pour prendre un nombre au hasard */
  int nl;/* pour récupérer la valeur lue */
  int temporaire; /* Pour éviter de faire plusieurs fois les même calculs */
  printf ("\nVeuillez entrer un nombre entre 0 et 1000: ");

  nc = hasard(1,1000);
  close(tube[1]); /* Le fils n'écrira jamais dans le tube */

  while(read(tube[0], &nl, sizeof(int))) {
    temporaire = Compare(nl,nc);
    alarm(0); /* Sinon,on réinitialise l'alarme */
    cpt++;
    if (temporaire==-1) { /* Le nombre entré n'est pas correct */
        fprintf(stderr, "Apprends a taper ! Tu perds un coup\n"); }
    else if (nl <= nbmin) {
        fprintf(stderr, "IL FAUT SUIVRE ! Le nombre a trouver est superieur a %d !!! Tu perds un coup\n", nbmin); }
    else if (nl >= nbmax) {
        fprintf(stderr, "IL FAUT SUIVRE ! Le nombre a trouver est inferieur a %d !!! Tu perds un coup\n", nbmax); }
    else if (temporaire==0){
        fprintf(stderr, "La valeur %d est trop petite (%d)\n",nl,nc);
    nbmin = nl;}
    else if (temporaire==1){
    fprintf(stderr, "La valeur %d est trop grande (%d)\n",nl,nc);
    nbmax = nl;}
    else {
        gereFichier();
    exit (0);}
        printf ("\nVeuillez entrer un nombre entre 0 et 1000: ");
        alarm(10);
        }

}

void finprocessus() {
   fprintf(stderr, "La partie est annulée.\n");
   wait(0);
   exit(0);
}

int main() {
  setbuf(stdout,NULL);
  signal(SIGALRM, alarm_handler);
  //signal(SIGINT, finprocessus);
  srand(getpid());

  if (pipe(tube)==-1) {
    printf("Erreur de création du tube\n");
    exit(-1);
  }

  switch(fork()) {

  case -1 :
    error("Erreur de création du fork\n");
    break;

  case 0 : // Je suis le fils
    bodyFils();
    break;
  }

  /* Je suis forcement le père */
  bodyPere();
  exit(0);
}




TP2 :
#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <unistd.h>
#include <signal.h>

/* Objets utilisés par le père et le fils */
int tube[2];
int compteur;
int Binf;/*les bornes seront partagées par les 2 processus*/
int Bsup;




void error(char *msg) {

  fprintf(stderr,msg);

  exit(-1);

}

int hasard(int bInf, int bSup) {
  return((rand() % (bSup+1-bInf)) + bInf);

}

void Plus_grand(int a){
    Binf=(Binf+Bsup)/2;}

void Plus_petit(int a){
    Bsup = (Binf+Bsup)/2;}

void Egalite(){
    exit(0);}

int Compare(int a, int b) {
  if (a<b) {
       return 0;}
  else if(a>b) {
       return 1;}
   else {
       return 2;}
}

void bodyPere() {
  int valcho;/*c'est la valeur choisie par le père et envoyée au fils*/
  Binf=0;
  Bsup= 1000;
  valcho=(Binf +Bsup)/2;
  signal (SIGUSR1, Plus_grand);
  signal (SIGUSR2, Plus_petit);
  signal (SIGKILL, Egalite);

  close(tube[0]); /* Le père ne lira jamais dans le tube */

  while(write(tube[1], &valcho, sizeof(int))) {
    sleep (1);
    valcho=(Binf +Bsup)/2;
   
  }
 
}



void bodyFils() {
  int cpt=1;
  int nb = hasard(1,1000);
  int vallue;/* pour lire la valeur que lui envoie le père*/
 
  close(tube[1]); /* Le fils n'écrira jamais dans le tube */

  while(read(tube[0], &vallue, sizeof(int))) {

    if (Compare(vallue,nb)==0) {/*valeur envoyée par le père est inférieure au nombre choisi*/
    fprintf (stderr,"la valeur %d est trop petite (%d) \n", vallue, nb);
    kill(getppid(),SIGUSR1);/*on envoie alors le signal sigusr1 au père*/}
    else if (Compare(vallue,nb)==1) {/*valeur envoyée par le père est supérieure au nombre choisi*/
    fprintf (stderr,"la valeur %d est trop grande (%d) \n", vallue, nb);
    kill(getppid(),SIGUSR2);/*on envoie alors le signal sigusr2 au père*/}
    else {
    fprintf (stderr,"Bravo, la valeur %d est la bonne (%d),  \n", vallue, nb);
    fprintf (stderr,"Le nombre a ete trouvé en %d coups \n", cpt);
    kill(getppid(),SIGKILL);/*on envoie alors le signal sigkill au père*/   
    exit(0);}
    cpt++;
    }
}


main() {

    setbuf(stdout, NULL); /* pour ne pas bufferiser les entrées sorties*/

    srand(getpid());/* pour le calcul du nombre aléatoire*/

  if (pipe(tube)==-1) {
    printf("Erreur de création du tube\n");
    exit(-1);
  }

  switch(fork()) {

  case -1 :
    error("Erreur de création du fork\n");
    break;

  case 0 : // Je suis le fils
    bodyFils();
    break;
  }

  /* Je suis forcement le père */
  bodyPere();
  exit(0);
}

Merci
matche

Hors ligne

#2 Le 20/01/2006, à 19:18

Bobbybionic

Re : programmation C : aide et conseils

Un peu "gore" comme sujet wink
Bon courage (je suis une cave en C wink)


Non à la vente liée. Non au monopole Windows.
Tous ensemble, refusons les logiciels préinstallés et tournons nous vers le libre.

http://bobbybionic.wordpress.com

Hors ligne

#3 Le 20/01/2006, à 19:56

m+rh-e

Re : programmation C : aide et conseils

Tu devrais vérifier que la saisie est bien un chiffre. (un test sur la valeur de retour de scanf)

man scanf

ps. compile avec l'option -Wall, ça permet de détecter des erreurs d'étourderies

else if (cpt >= ((tab[2]/tab[3])-2) & (cpt <= (tab[2]/tab[3])+2)) printf("Dans la moyenne...\n");
printf("\nStatistiques :\n",tab[0]);
wait(0)

Hors ligne

#4 Le 20/01/2006, à 20:31

matche

Re : programmation C : aide et conseils

Merci pour ces réponses... Et ces encouragement !
Moi, j'ai bien aimé le sujet, j'aime bien programmer du nouveau, ça change des sites internet et du Director !
Mais bon, c'est pas forcément plus facile, au contraire... smile

J'ai utilisé l'option -Wall ... Ca a l'air utile: il me donne une 60aine de ligne à la compilation...
Mais je n'en n'ai pas saisie le sens... Pas évident : Il me parle de variables que je n'ai jamais utilisé mais qui sont "définies pour la première fois ici" ou qui ont des définitions multiples"...
Par contre, j'ai vu qu'il donnait plus d'information à la fin sur la fonction gerefichier :
/usr/bin/ld: Warning: size of symbol `gereFichier' changed from 524 in /home/matche/Desktop/cnam/prog/TP/projet_exo1.exe to 528 in /tmp/ccMR7ZTl.o
Cela signifie qu'il y a une erreur dans le gestionnaire, mais ne précise rien de plus, si ?
Sinon, je j'ai pas le namuel de scanf : "Aucune entrée pour scanf ... Pas normal je suppose ???
Merci encore
Matche

Hors ligne

#5 Le 20/01/2006, à 21:21

m+rh-e

Re : programmation C : aide et conseils

pour les mans, il faut installer manpages-dev
autrement, avec les lignes de codes que tu proposes je n'obtiens pas autant de warning

/usr/bin/ld: Warning: size of symbol `gereFichier' changed from 524 in /home/matche/Desktop/cnam/prog/TP/projet_exo1.exe to 528 in /tmp/ccMR7ZTl.o

c'est une erreur au niveau du linkage, il faut peut être recompiler toutes les sources

la construction d'un binaire se fait en plusieurs étapes
-> compilation des fichiers sources en fichiers objets (*.o)
-> linkage libs + *.o en fichier binaire

Hors ligne

#6 Le 22/01/2006, à 11:10

mrf

Re : programmation C : aide et conseils

je peux poser une question con ?

Pourquoi tu fais un .exe si ton chemin est /home/... ( donc un système unix)

Sinon, concernant tes erreurs, t'as qu'un seul fichier ? ou t'as splitté ton code ? Les erreurs dont tu parles sont du style 'variable ou fonction non définie' Pour utiliser une fonction ou une variable, n'oublie pas de la définir avant (pour les fonctions un header (.h) peut aider)

#7 Le 22/01/2006, à 14:25

matche

Re : programmation C : aide et conseils

Salut !
J'ai trouvé la valeur retournée par scanf lorsqu'il y a une erreur d'entrée :0, tout simplement.
En fait, je n'avais pas tant de ligns d'erreurs que ça :  j'utilisais mal la fonction. Je n'ai plus aucune ligne d'erreur, donc tout va bien !

Pour ta question, mrf, oui, c'est un peu con de créer un .exe. En fait, c'est parce qu'avant, j'étais sous cygwin et du coup, je pouvais lancer mon prog sous windows.
Mais c'est vrai que maintenant que je suis sur ubuntu, je n'en n'ai plus trop besoin, mais par habitude, j'y suis resté big_smile

Sinon, je n'ai qu'un seul fichier et plus d'erreur... Du moins, d'erreur affichées.
Par contre, j'ai toujours 2 problème qu'il faut que je résolve vite : je dois envoyer le TP ce soir.
Donc, mes 2 problèmes sont toujours les memes :
1/ Lorsque je rentre une lettre, il m'affiche la phrase "désolé il faut savoir taper sur un clavier pour jouer", et il tourne en boucle sans me laisser le temps de retaper une touche. J'ai tester le code de retour de scanf, et ça fonctionne, mais il ne me laisse plus l'occasion de corriger !
2/ Il ne me laisse plus la main lorsque la partie est finie : il faut que j'appuie sur une touche, que je refasse entrée !
Si quelqu'un a une idée, ce serait vraiment cool...
Avec le nouveau code, c'est mieux big_smile

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <errno.h>

#include <unistd.h>

#include <signal.h>





/* Objets utilisés par le père et le fils */

  int tube[2];

  int cpt=0; /* c'est le compteur de nombre de coups joués */

  int nbmin = 0;

  int nbmax = 1000;/* définition de l'intervalle de saisie */



void gereFichier() {/*gestion des scores*/

  int tab[4] = {cpt, cpt, cpt, 1};

  FILE *fp = fopen("scores.txt", "r+b");



  if (fp != NULL) {

     fread(tab, sizeof(int), 4, fp);

     if (cpt < tab[0]) tab[0] = cpt;

     if (cpt > tab[1]) tab[1] = cpt;

     tab[2] = tab[2] + cpt;

     tab[3] = tab[3] + 1;

     fclose(fp);



 }



fp = fopen("scores.txt", "wb");

fwrite(tab, sizeof(int), 4, fp);



fprintf(stderr, "Bravo, c'est le bon chiffre, tu as trouve en %d coups.\n", cpt);



if (cpt == tab[0]) printf("Felicitations, tu viens de pulveriser le score !!!\n",tab[0]);

else if (cpt == tab[1]) printf("Pffff, jamais eu de score aussi minable !!!\n",tab[0]);

else if (cpt >= ((tab[2]/tab[3])-2) & (cpt <= (tab[2]/tab[3])+2)) printf("Dans la moyenne!!!\n",tab[0]);

printf("\nStatistiques :\n");

printf("Le nombre de coups minimum est %d\n",tab[0]);

printf("Le nombre de coups maximum est %d\n",tab[1]);

printf("Le nombre de coups moyen est %d / %d\n",tab[2], tab[3]);



// on ferme le fichier

fclose(fp);



exit(0);

}





void error (char *msg) {

  fprintf(stderr,"%s",msg);

  exit(-1);

}



int hasard(int bInf, int bSup) {

  return((rand() % (bSup+1-bInf)) + bInf);



}



int Compare(int a, int b) {

/* pour comparer la valeur entrée par l'utilisateur et 

  celle trouvée par la machine */

  if (a<b) {

       return 0;}

  else if(a>b) {

       return 1;}

   else {

       return 2;}

}



void alarm_handler(int nb){

  /*fonction qui a pour résultat de rajouter un coup si les 10 secondes sont dépassées*/

  printf("\nL'heure tourne, plus vite l'escargot ! Vous avez un coup en moins.\n");

  printf ("Veuillez entrer un nombre entre 0 et 1000: \n");

  cpt++;

  alarm(10);

}



void mauvaisFormat(){

  printf("\nDésolé, il faut savoir taper sur un clavier pour jouer. Vous avez un coup en moins.\n");

  printf ("Veuillez entrer un nombre entre 0 et 1000: \n");

  cpt++;

  alarm(10);

}



void bodyPere() {

  int nb;
  int resultat;

  close(tube[0]); /* Le père ne lira jamais dans le tube */

  while(1) {

      signal(SIGALRM,SIG_IGN);/*pour stopper les alarmes déjà lancées*/

      resultat = scanf ("%d",&nb); /* saisie du nombre */
      if (resultat != 0) {

        write(tube[1], &nb, sizeof(int));}
      else {
mauvaisFormat();
resultat = 0;
nb=0;}

    }

}



  

void bodyFils() {

  int nc;/* pour prendre un nombre au hasard */

  int nl;/* pour récupérer la valeur lue */

  int temporaire; /* Pour éviter de faire plusieurs fois les même calculs */

  printf ("Veuillez entrer un nombre entre 0 et 1000: \n");



  nc = hasard(1,1000);

  close(tube[1]); /* Le fils n'écrira jamais dans le tube */



  while(read(tube[0], &nl, sizeof(int))) {

    temporaire = Compare(nl,nc);

    alarm(0); /* on réinitialise l'alarme */

    cpt++;

    if (nl <= nbmin) {

        fprintf(stderr, "IL FAUT SUIVRE ! Le nombre a trouver est superieur a %d !!! Tu perds un coup\n", nbmin); }

    else if (nl >= nbmax) {

        fprintf(stderr, "IL FAUT SUIVRE ! Le nombre a trouver est inferieur a %d !!! Tu perds un coup\n", nbmax); }

    else if (temporaire==0){

        fprintf(stderr, "La valeur %d est trop petite \n",nl);

	nbmin = nl;}

    else if (temporaire==1){

	fprintf(stderr, "La valeur %d est trop grande \n",nl); 

	nbmax = nl;}

    else {

        gereFichier();

		



	exit (0);}

        printf ("Veuillez entrer un nombre entre 0 et 1000: \n");

        alarm(10);

        }



}



void finprocessus() {

   fprintf(stderr, "La partie est annulée.\n");

   wait(0);

   exit(0);

}



int main() {

  setbuf(stdout,NULL);

  signal(SIGALRM, alarm_handler);

  signal(SIGINT, finprocessus);

  srand(getpid());



  if (pipe(tube)==-1) {

    printf("Erreur de création du tube\n");

    exit(-1);

  }



  switch(fork()) {



  case -1 : 

    error("Erreur de création du fork\n");

    break;



  case 0 : // Je suis le fils

    bodyFils();

    break;

  }



  /* Je suis forcement le père */

  bodyPere();

  exit(0);

}

Merci
Matche

Dernière modification par matche (Le 22/01/2006, à 14:59)

Hors ligne

#8 Le 23/01/2006, à 17:12

m+rh-e

Re : programmation C : aide et conseils

man scanf
la valeur renvoyée correspond au nombre  de conversion réussie.
ici scanf lit dans le flux d'entré, si il trouve un chiffre la conversion sera faite et le flux sera vidé, sinon le flux reste en état. Il faut par conséquent vidé le flux d'entré.

if ( scanf ("%d",&nb) != 1 ) /* 1 conversion attendue */
   {  /* erreur de saisie, on vide le flux */
      int c;
      while ( ((c = getchar()) != '\n') && c != EOF);
   }

Hors ligne

#9 Le 23/01/2006, à 20:04

matche

Re : programmation C : aide et conseils

Génial, ça marche big_smile
Merci beaucoup m+rh-e !!! 

Reste plus que le problème que le programme ne quitte pas immédiatement lorsque le jeu est fini...
Quelqu'un a une idée ???

Hors ligne

#10 Le 23/01/2006, à 21:39

m+rh-e

Re : programmation C : aide et conseils

Pour plus de lisibilité de ton code tu devrais utiliser des astuces telles que

#define IN  0
#define OUT 1

read(tube[IN], , );
write(tube[OUT], , );

exit(EXIT_SUCCESS);
exit(EXIT_FAILURE);
cf. man exit

Autrement, ton problème provient de ton design, il serait plus judicieux de mettre le serveur de ton jeu dans le thread père et le client qui gère la saisie dans le thread fils

Dernière modification par m+rh-e (Le 23/01/2006, à 21:40)

Hors ligne

#11 Le 30/01/2006, à 15:51

jerikojerk

Re : programmation C : aide et conseils

C'est une erreur de vouloir mélanger des "write"  et des "scanf" sur certain man le bug est signalé.
soit ont fait des sprintf/scanf soit on utilise les appels systemes read / write ....

http://www.linux-kheops.com/doc/man/manfr/man-ascii-0.9/man3/fgets.3.txt.html

Il est fortement déconseillé de mélanger  les  appels  aux fonctions  de  lecture  de  la bibliothèque stdio avec les appels aux fonctions de lecture bas-niveau read()  sur  le descripteur de fichier associé au flux. Les résultats sont indéfinis, et très probablement indésirables.

Haut de page ↑