Mois: Décembre Sujet: Boucles, manipulation de tableaux et pointeurs
zMorp - 1ère partie
Je vais tenter une approche un peu différente pour essayer d'attirer plus de monde cette fois ci.
Le but de l'exercice de ce mois est de créer le célèbre jeu du morpion ! La première partie (mois de décembre) ne portera que sur le jeu ! La seconde partie portera sur l'intelligence artificielle, chose qui intrigue pas mal de monde. Mais avant l'IA, il faut faire le jeu.
Les règles sont simple :
On a un plateau de 3x3 cases,
On tire un joueur au hasard pour savoir qui va commencer la partie,
Les joueurs placent chacun un pion (croix ou rond selon le joueur) chacun leur tour,
Le premier qui fait une ligne de 3 pions gagne la partie !
Si il n'y a plus de place sur le plateau pour mettre un pion, il y a match nul.
Voici un exemple d'exécution :
Joueur 1 commence ! <-- Récupéré aléatoirement
. . . <-- Affichage du plateau
. . .
. . .
Joueur 1 : 5 <-- Récupère la saisie de l'utilisateur
. . . <-- Affiche le plateau avec le coup joué
. X .
. . .
Joueur 2 : 1 <-- Ainsi de suite
O . .
. X .
. . .
Joueur 1 :
Vous ne ferez qu'un mode 2 joueurs pour l'instant. Le mode 1 joueur sera vu dans la seconde partie (un autre exo).
Pour vous faciliter la tâche vous pouvez utiliser un tableau simple dimension. Si vous vous sentez plus à l'aise (ou que vous avez fini le jeu avec un tableau simple) vous pouvez essayer d'utiliser un tableau à 2 dimensions. Je vous conseille vivement de ne pas vous embêter avec les saisies utilisateur ! Ce n'est pas le but du programme !
Récupérez un nombre correspondant à la case et à d'autres options que vous aurez déterminées (recommencer, rejouer, annuler le coup, etc.) et c'est tout. N'allez pas chercher à récupérer les coordonnées x et y ou d'autres options, ce n'est pas le but.
Pour commencer vous pouvez, par exemple, utiliser ces fonctions (ce plan est vague mais permet d'avoir un point de départ) :
jouer : Contient la boucle principale du jeu
affichage : Affiche le plateau de jeu
saisie : Récupère les coordonnées saisies par l'utilisateur
testerCoup : Teste si le coup peut être joué (si la case est vide)
jouerCoup : Joue le coup
(optionnel) supprimerCoup : Vous pouvez mémoriser les coordonnées saisies afin de revenir en arrière si l'utilisateur en fait la demande (en entrant une valeur spéciale, -1 par exemple)
testerGagnant : Regarde si il y a un gagnant (ou pas -> match nul) sur le plateau. 4 cas possibles : 1. On peut continuer à jouer 2. Le joueur 1 à gagné 3. Le joueur 2 à gagné 4. Il y a match nul
Vous n'êtes en aucun cas obligé de l'utiliser, c'est surtout pour les personnes qui auraient du mal à commencer.
Le but de cet exercice est d'amener les gens à voir autre chose que le jeu présenté dans la partie SDL. J'espère que cette fois il y aura plus de participants, et surtout plus de débutant. Ce n'est pas un exercice difficile, il faut simplement utiliser un papier et un crayon et tout mettre à plat avant de se lancer dans la programmation. Si vous foncez tête baissée sur la programmation sans avoir utilisé le papier vous aurez 2 options : soit vous partez dans le mur, soit vous ferez un programme bancal (soit vous l'avez déjà programmé et ça sera correct).
Encore une fois, ce n'est pas difficile ! Un exercice résolu en 5 minutes est inutile, un exercice où on se prend la tête nous apporte beaucoup.
Je ne donne pas de code de départ, mais si le besoin se fait sentir j'en posterai un quand même. Ce n'est pas une course, faites un code propre, lisible et sans bugs. Essayez de faire en sorte d'avoir un code qui peut évoluer avec le temps, c'est-à-dire que si je puisse rajouter et enlever des choses sans que ça impacte sur tout le programme.
Evolutions
Nous sommes partis sur un morpion de 3x3 cases, maintenant faites un morpion qui pourrait avoir 5x5, 10x10 ou XxX cases (ne demandez pas à l'utilisateur on s'en fou, c'est simplement pour faire un programme évolutif).
Bon courage à tous et n'hésitez pas à demander de l'aide si vous avez un doute ou une question, nous sommes là pour y répondre.
Voilà une bonne idée d'exercice pour s'entrainer !
Malheureusement, je n'ai vraiment pas le temps en ce moment de programmer. Comme c'est l'exo "du mois de décembre", je tenterai de poster mon code pendant les vacances de Noël si je trouve un peu de temps libre pour m'y coller.
Même si mon post, au final ne sert à rien, je ties également à remercier tous ceux qui se creusent la tête pour proposer des exercices pour les débutants. J4ai déjà commencé ceux du début, je me suis arrêté au zAddition.
Bref, un message général pour remercier tous ceux qui participent à la création des exercices
Une remarque sur ton code.
Ta fonction testerGagnant teste toutes les combinaisons, alors qu'il suffit de ne prendre en compte que le dernier coup joué.
Tu n'as besoin de tester que
la ligne du dernier coup joué
la colonne du dernier coup joué
2 diagonales
edit:
pour un morpion ça ne change pas grand chose, c'est vrai.
@che, ne met peut-être pas ton code pour permettre à toute la communauté de réfléchir
C'est comme cela que l'on a toujours fait.
Exemple zcrypt, et aussi je l'ai mis entre les balises secret.
@GurneyH: Ouais je n'y avais pas pensé, c'est claire que ça ne change pas grand chose mais je vais voir si j'ai un peu plus de temps pour le faire (surtout que cette fonction m'a fait déjà perdre beaucoup de temps à cause d' ';' après un boucle for, à cause d'une parenthèse enfin un truck bête quoi. C'est pour ça qu'elle est mal faite(niveau condition).
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles- ♡ Copying is an act of love.
@che > Quelques remarques :
Tu pourrais faire en sorte de pouvoir rejouer. (ce n'est pas obligatoire, ce n'est pas demandé dans l'exercice)
Tu pourrais ne pas utiliser de variable globale.
Tu répètes 2 fois la même chose dans ta boucle du main (joueur 1 et joueur 2), tu es sûr de ne pas pouvoir raccourcir ?
Tu appelles plein de fois ta fonction 'nul' pour savoir si tu es en match nul, tu pourrais utiliser une variable pour éviter de rappeler ta fonction.
Dans ta fonction testerGagnant, tu testes si tes cases sont égales et aussi si elles sont égales à '.' pour les 3 cases, tu n'as pas besoin ! Si les 3 sont égaux, il te suffit de tester qu'une seule case est différente de '.'.
Petite faute de français, on dit 'Joueur %d à gagné'
Toujours dans la même fonction, tu fais 2 boucles pour tester tes colonnes/lignes, tu pourrais n'en utiliser qu'une seule.
Dans ta fonction testerCoup, il faut que tu fasses l'inverse. Là, tu testes d'abord ton tableau et ensuite si ton x est compris dans l'intervalle du tableau. Si x n'est pas compris dans l'intervalle du tableau tu testes quand même ton tableau.
Autre chose par rapport à ça, tu as un tableau 2D de 3x3, en faisant tab[0][x-1] tu fais comme si tu avais un tableau 1D, ce n'est pas terrible. Si jamais tu veux changer ton tableau en tableau de pointeurs tu risques fort d'avoir des problèmes.
Tu devrais mettre 'void' dans tes fonctions qui ne prennent aucun paramètre.
C159 > Quelques remarques :
Tu devrais mettre 'void' dans ta fonction main.
Pour les noms de variable, on préfèrera utiliser des noms minuscules, les noms majuscules étant utilisés pour des macros (ce n'est en aucun cas une obligation, c'est plus une convention utilisée par beaucoup de gens).
Tu utilises la récursion pour ta fonction jouer, pourquoi ?
On préfèrera déclarer les variables en début de bloc et pas en plein milieu d'une fonction.
Tu utilises aussi la récursion pour la fonction saisiCASE, ça n'offre aucun intérêt.
Tu utilises les ternaires, mais essaye de garder un code lisible. 1 ternaire ça va, 2 ternaires imbriqués ça rend le code moins lisible encore une fois, aucune obligation, c'est juste pour la lisibilité pour toi et pour les autres).
Tu pourrais éviter les variables globales.
Ta fonction jouer retourne quelque chose mais tu ne le récupère pas dans ta fonction main.
Utiliser cette écriture est assez désagréable à lire (!(tableau[i]=='O'||tableau[i]=='X')), tu pourrais simplement la remplacer par (tableau[i]!='O'&&tableau[i]!='X') (même chose dans ta fonction saisiCASE).
Petite faute de français, on dit 'Joueur XX à gagné'
Tu pourrais éviter de tout mettre sur une ligne et que ça fasse 200 caractères de long. Ca évite de scroller horizontalement et verticalement, c'est assez désagréable. Ta fonction d'affichage est plus agréable à lire comme ça :
Bon voila le nouveau code, j'ai un peu modifié avec toutes les modifications sauf celle de GurneyH et celles de Pouet_Forever traitant de testerGagnant():
#include <stdio.h>
#include <stdlib.h>
int testerCoup(int x, int tab[][3])
{
return x<10&&x>0&&!tab[0][x-1];
}
void jouerCoup(int x,char c, int tab[][3])
{
tab[0][x-1] = c;
}
int testerGagnant(int tab[][3])
{
int i;
if((tab[0][0]+tab[1][1]+tab[2][2] == 3*tab[0][0]) && tab[0][0])
return printf("Joueur %d a gagne\n", 1+(tab[0][0]=='O'));
if((tab[0][2]+tab[1][1]+tab[2][0] == 3*tab[0][2]) && tab[0][2])
return printf("Joueur %d a gagne\n", 1+(tab[0][2]=='O'));
for(i=0;i<3;i++)
if((tab[i][0]+tab[i][1]+tab[i][2] == 3*tab[i][0]) && tab[i][0])
return printf("Joueur %d a gagne\n", 1+(tab[i][0]=='O'));
for(i=0;i<3;i++)
if((tab[0][i]+tab[1][i]+tab[2][i] == 3*tab[0][i]) && tab[0][i])
return printf("Joueur %d a gagne\n", 1+(tab[i][0]=='O'));
return 0;
}
void show(int tab[][3])
{
int x,y;
for(y=0;y<3;y++,printf("\n"))
for(x=0;x<3;x++)
printf("%c",(tab[y][x])?:'.');
}
int main(void)
{
int c, u;
do
{
u=c=0;
{
int tab[3][3] = {0};
do
{
printf("Joueur %d :",u%2+1);
do
{
scanf("%d",&c);
}while(!testerCoup(c,tab));
jouerCoup(c,(u%2?'O':'X'),tab);
show(tab);
u++;
}while(!testerGagnant(tab) && u!=9);
}
if(!(u-9))
printf("Match nul\n");
c=0;
puts("Voulez-vous rejouer ? 0 pour non :");
scanf("%d",&c);
}while(c);
return EXIT_SUCCESS;
}
Citation : Pouet_forever
Dans ta fonction testerGagnant, tu testes si tes cases sont égales et aussi si elles sont égales à '.' pour les 3 cases, tu n'as pas besoin ! Si les 3 sont égaux, il te suffit de tester qu'une seule case est différente de '.'.
Oui évidemment mais c'est à cause d'une erreur bête qui m'a forcée à mettre 4-5 conditions différentes et finalement j'ai mis cela(et comme ça ne marchait toujours pas donc je savais que ça venait pas de la condition), enfin une connerie quoi.
Citation : Pouet_forever
Tu devrais mettre 'void' dans tes fonctions qui ne prennent aucun paramètre.
Citation : Pouet_forever
Tu pourrais ne pas utiliser de variable globale.
Soit l'un soit l'autre.
Je modifierais testerGagnant() pour ne tester qu'un ligne, qu'une colonne plus tard.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles- ♡ Copying is an act of love.
Bon voila le nouveau code, j'ai un peu modifié avec toutes les modifications sauf celle de GurneyH et celles de Pouet_Forever traitant de testerGagnant()
Et sauf celles concernant le fait d'avoir un tableau 2D et de l'utiliser comme un tableau 1D (fonctions testerCoup et jouerCoup).
Pour ta fonction testerGagnant, c'est mieux de tester comme tu l'avais fait au début (case1 = case2 = case3 et case1 != '.' (0 maintenant)). Si tu décides de changer tes valeurs '0' et cie par 0 et cie tu risques d'avoir des problèmes avec tes 3*XX.
Tu peux regrouper tes for dans ta fonction testerGagnant.
T'es sûr que tu peux pas faire mieux if(!(u-9)) ?
Il te manque quelque chose là : printf("%c",(tab[y][x])?:'.');
Le fait d'avoir une fonction qui ne prend aucun paramètre n'a rien à voir avec l'utilisation des variables globales.
Ce n'est pas une course, arrêtez de poster le plus rapidement possible, ça ne sert à rien...
Faites un code propre, découpez un maximum votre code en fonctions et essayez de faire un code modifiable à souhait (si je modifie la taille de la grille vos codes sont jetable).
Je vais rajouter une partie dans l'exercice du départ pour éviter les codes fait à la va-vite.
Je me répète : Ce n'est pas une course, faites un code propre, lisible et sans bugs. Essayez de faire en sorte d'avoir un code qui peut évoluer avec le temps, c'est-à-dire que si je puisse rajouter et enlever des choses sans que ça impacte sur tout le programme.
Je modifie l'énoncé.
Pour ta fonction testerGagnant, c'est mieux de tester comme tu l'avais fait au début (case1 = case2 = case3 et case1 != '.' (0 maintenant)). Si tu décides de changer tes valeurs '0' et cie par 0 et cie tu risques d'avoir des problèmes avec tes 3*XX.
De toute façon j'ai tout changer.
Citation : Pouet_forever
T'es sûr que tu peux pas faire mieux if(!(u-9)) ?
Si, c'est le concour d'obfuscation qui fait ça .
Citation : Pouet_forever
Il te manque quelque chose là : printf("%c",(tab[y][x])?:'.');
Ah bon ? il y a un risque d’erreur ? Surtout que je trouve rien dans la norme à ce sujet.
Citation : Pouet_forever
Le fait d'avoir une fonction qui ne prend aucun paramètre n'a rien à voir avec l'utilisation des variables globales.
Oui c'est vrai mais ce que je voulais dire c'était que j'avais plus de fonction sans paramêtre si je retirais les variables globales.
Citation : Pouet_forever
Faites un code propre, découpez un maximum votre code en fonctions et essayez de faire un code modifiable à souhait (si je modifie la taille de la grille vos codes sont jetable).
La normalement ça devrait être bon.
Citation : Pouet_forever
Je me répète : Ce n'est pas une course, faites un code propre, lisible et sans bugs. Essayez de faire en sorte d'avoir un code qui peut évoluer avec le temps, c'est-à-dire que si je puisse rajouter et enlever des choses sans que ça impacte sur tout le programme.
Je modifie l'énoncé.
J'ai essayé tu me dira ce que tu en penses.
Voila le code changé :
#include <stdio.h>
#include <stdlib.h>
#define TAILLE_X 4
#define TAILLE_Y 4
int testerCoup(int x, int tab[][TAILLE_X])
{
return x < (TAILLE_X*TAILLE_Y+1) && x >= 0 && !tab[x/TAILLE_X][x%TAILLE_X];
}
void jouerCoup(int x,char c, int tab[][TAILLE_X])
{
tab[x/TAILLE_X][x%TAILLE_X] = c;
}
int testerGagnant(int tab[][TAILLE_X], int x, int user)
{
int i=0;
char c = 0;
if(TAILLE_Y==TAILLE_X)
{
if(tab[0][0])
for(i=1,c = tab[0][0];i<TAILLE_Y;i++)
if(c!=tab[i][i])
break;
if(i==TAILLE_Y)
return printf("Joueur %d a gagne\n", user);
if(tab[0][TAILLE_X-1])
for(i=1,c = tab[0][TAILLE_X-1];i<TAILLE_Y;i++)
if(c!=tab[i][TAILLE_X-i-1])
break;
if(i==TAILLE_Y)
return printf("Joueur %d a gagne\n", user);
}
for(i=1,c = tab[x/TAILLE_X][0];i<TAILLE_X;i++)
if(c!=tab[x/TAILLE_X][i])
break;
if(i==TAILLE_X)
return printf("Joueur %d a gagne\n", user);
for(i=1,c = tab[0][x%TAILLE_X];i<TAILLE_Y;i++)
if(c!=tab[i][x%TAILLE_X])
break;
if(i==(TAILLE_Y))
return printf("Joueur %d a gagne\n", user);
return 0;
}
void show(int tab[][TAILLE_X])
{
int x,y;
for(y=0;y<TAILLE_Y;y++,printf("\n"))
for(x=0;x<TAILLE_X;x++)
printf("%c",(tab[y][x])?:'.');
}
int main(void)
{
int c, u;
do
{
u=c=0;
{
int tab[TAILLE_Y][TAILLE_X] = {0};
do
{
printf("Joueur %d :",u%2+1);
do
{
scanf("%d",&c);
c--;
}while(!testerCoup(c,tab));
jouerCoup(c,(u%2?'O':'X'),tab);
show(tab);
u++;
}while(!testerGagnant(tab, c, (u-1)%2+1) && u != (TAILLE_X * TAILLE_Y));
}
if(u == (TAILLE_X * TAILLE_Y))
printf("Match nul\n");
c=0;
puts("Voulez-vous rejouer ? 0 pour non :");
scanf("%d",&c);
}while(c);
return EXIT_SUCCESS;
}
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles- ♡ Copying is an act of love.
Le modulo 10000000 est inutile, le %2 est amplement suffisant : intjoueur=(rand()%10000000)%2;
Ta fonction quel_Joueur laisse paraitre qu'elle ne donne que le joueur qui va commencer, mais elle sert aussi de fonction d'affichage, tu pourrais sortir ça de ta fonction. Tu pourrais même retourner simplement le joueur qui va commencer.
Le fait de déclarer ta structure dans le main et de la passer en pointeur à ta fonction jouer ne sert à rien, autant la déclarer directement dans ta fonction jouer.
C'est mieux de mettre void en paramètre de fonction quand elle ne prend aucun paramètre.
Dans ta fonction saisie_Ok, tu pourrais tester si l'indice n'est pas négatif ou nul.
Tu utilises m->joueur_Actuel avec des 0 et des 1, pourquoi ne pas utiliser les enums J1 et J2 déclarés plus haut ?
Même remarque que @che pour ta fonction gagne, tu testes si tes 3 cases sont égales, il te suffit simplement de tester 1 seule case pour voir si elle est différente de VIDE.
Dans le printf après ton if tu as une séquence inconnue '\L'. Tu as sûrement mis un \ en trop.
Dans ta fonction mise_A_Zero tu t'es trompé sur une instruction : m->etat==KEEP_ON;
Ta condition pour tester si il y a match nul est fausse, il faudrait mettre un && et non pas un ||.
Maintenant que tu as fait le jeu pour 3x3 cases, tu peux le faire évoluer afin d'avoir un nombre quelconque de cases.
En tout cas bravo, ton code est propre et bien structuré.
Bon courage.
les exos ne sont plus en post it ? C'est un bug ?Je bosse sur l'exo, et je fournirais ma humble participation
à savoir 10 h de reflexion, et 1h de codage
J'ai utilisé "% 10000000" car selon ce tuto, lorsque l'on fait int joueur = rand() % 2; on a plus de chance de tomber sur un 0 que sur un 1.
Pour le reste, je suis entièrement d'accord avec toi ( sachant que je ne suis pas totalement sur de ce que j'ai avancé plus haut ... ).
Je pense l'avoir corrigé par rapport aux remarques de Pouet forever.
J'espère vraiment qu'il y aura plus de participation qu'aux autres exos !
Edit : Je vais effectuer quelques recherches mais pourquoi lorsque j'effectue un copier-coller de mon code et que je le met ici, il y a des tabulations supplémentaires qui s'y ajoute ( j'ai corrigé le code code ci-dessus manuellement; je suis sous Mozilla Firefox ) ?
Utilises le %2, avec les dernières versions de rand tu auras autant d'équiprobabilité (je sais pas si ça se dit ).
Pour les tabulations, c'est simplement que le SDZ considère qu'une tabulation fait 8 espaces et pas 4 (pourquoi ? je sais pas mais c'est très déplaisant).
@paraze :
time attend un pointeur.
Dans ta fonction 'jouer', tu devrais faire ta mise à zéro en début de boucle.
Dans ta fonction saisie_Ok, ton test <= 0 doit être placé avant l'accès au tableau.
Pour le reste, je ne vois rien à redire.
Maintenant tu pourrais essayer de faire évoluer ton code pour pouvoir gérer les tableaux de n'importe quelle taille.
@DuaL :
Pour ta fonction displayGrid tu pourrais utiliser une boucle.
PlayerNumber est de type int, ta fonction choix attend un unsigned char. Ici il n'y a aucun problème, mais fais attention à ça si tu utilises des valeurs plus grandes.
Tu ne fais pas assez de vérifications sur les entrées de l'utilisateur. Je peux passer mon tour en jouant une case déjà jouée.
Dans ta fonction 'choix' tu utilises le même nom de variable, modifies le nom pour qu'il n'aie pas le même nom que la fonction.
Dans ta fonction victoryTestor il faudrait que tu mettes des parenthèses à tes conditions.
Dans la même fonction, il n'est pas très judicieux d'utiliser un switch, une suite de if aurait été plus adapté. Par ex : si l'utilisateur entre 0 ou 1 ou 2 -> on teste cette ligne, si l'utilisateur entre 3 ou 4 ou 5 -> on teste cette ligne, etc. . Ca allègerait ton code.
Il faudrait que tu revoies tes tests en fin de fonction, ça me parait faux au premier coup d'oeil.
Faudrait peut-être mettre les règles et les crédits.
Plutôt que faire appel à exit, tu peux simplement retourner dans le main.
Les commentaires inutile sont... inutiles. Un commentaire doit éclaircir certaines parties du code qui semblent compliquées.
Tu utilises plein de variables, pourquoi pas un tableau ?
choixJ1 et choixJ2 sont 2 fonctions qui font exactement la même chose, quel intérêt d'avoir fait 2 fonctions distinctes ?
Tu fais trop de répétitions de code, c'est à ça que servent les fonctions : éviter le copier/coller.
Tu fais de la récursivité dans ta fonction de jeu, quel intérêt ?
Je n'ai pas regardé en profondeur ton code, les répétitions à gogo le rendent illisible.
Les fonctions qui ne prennent aucun paramètre doivent avoir 'void' en paramètre.
Faire toutes tes inclusions de headers dans un seul fichier n'est pas terrible. Inclus tes headers dans tes fichiers .c qui en ont besoin (certains font comme toi, mais personnellement je n'aime pas).
@darkipod : Un morpion NxN est un morpion avec N cases de longueur et N cases de largeur. Il faut aussi fixer le nombre de pions qu'il faudra aligner pour gagner. Par exemple, avec un plateau de 10x10, avoir 3 pions alignés est trop simple, il faudrait en avoir 5 pour avoir un minimum de challenge.
Un morpion W x H. Enfin, il faut éviter les grilles de dimensions > 9.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define W 3 /* Largeur de la grille */
#define H 3 /* Hauteur de la grille */
#define N 3 /* Nombre de pions à aligner */
/* I/O --------------------------------------------------------------------------------- */
static void clear(void)
{
int c;
while((c = getchar()) != '\n' && c != EOF)
;
}
/* Une autre partie ? */
int IO_ask(void)
{
char c;
for(;;)
{
int ret;
printf("Une autre partie (o/n) ? ");
ret = scanf("%c", &c);
clear();
if(ret != 1)
puts("Saisie invalide !");
else
{
if( c == 'O' || c == 'o')
return 0;
else if(c == 'N' || c == 'n')
return 1;
}
}
}
void IO_displayMsg(char const *msg)
{
printf("%s", msg);
}
/* Retourne la case choisie par le joueur courant */
int IO_getChoice(int *grid, int p)
{
int row, col;
/* On boucle tant qu'on a pas une saisie jugée valide */
for(;;)
{
int ret;
printf("Joueur %d (ligne colonne) -> ", p);
ret = scanf("%d %d", &row, &col);
clear();
if(ret != 2)
puts("Saisie invalide !");
else
{
if((unsigned)--row >= H || (unsigned)--col >= W)
puts("Ligne ou colonne incorrecte");
else if(grid[row * W + col])
puts("Case occupée");
else
return row * W + col;
}
}
}
/* Affiche une ligne de - aux bonnes dimensions */
void separator(void)
{
int i;
for (i = 0; i < W; i++)
printf(" -");
puts("");
}
/* Affiche la grille */
void Grid_display(int *grid)
{
int i, j;
for(i = 0; i < H; i++)
{
separator();
for(j = 0; j < W; j++)
{
printf("|");
putchar(" 0X"[grid[i * W + j]]);
}
puts("|");
}
separator();
}
/* JEU ---------------------------------------------------------------------- */
/* Teste une direction en partant d'une cellule donnée */
int lower_check(int *grid, int cell, int ii, int ij)
{
int i = cell / W;
int j = cell % W;
int count = 0;
while((unsigned)i < H && (unsigned)j < W)
{
if(grid[i * W + j] == grid[cell])
{
count++;
i += ii;
j += ij;
}
else
break; /* On est tombé sur un pion adverse, on sort */
}
return count - 1;
}
/* Testes toutes les directions à partir d'une case donnée.
* Retourne 1 si gagnant, 0 sinon.
*/
int check(int *grid, int cell)
{
/* Directions */
static int a_inc[4][2] = {{1, 0}, {0, 1}, {1, 1}, {1, -1}};
int i;
for(i = 0; i < 4; i++)
{
int ii = a_inc[i][0]; /* Direction sur l'axe H */
int ij = a_inc[i][1]; /* Direction sur l'axe W */
int count = 1; /* On compte le pion qui vient d'être placé */
count += lower_check(grid, cell, ii, ij);
count += lower_check(grid, cell, -ii, -ij);
/* Si on a aligné N pions la partie est terminée. */
if(count >= N)
return 1;
}
/* La partie continue */
return 0;
}
/* Boucle principale */
void play(void)
{
char buf[BUFSIZ];
int grid[W * H] = {0};
int crtPlayer = rand() % 2 + 1;
int count = 0;
int win = 0;
sprintf(buf, "Le joueur %d commence!\n", crtPlayer);
IO_displayMsg(buf);
while(count++ < W * H)
{
int cell;
Grid_display(grid);
cell = IO_getChoice(grid, crtPlayer);
grid[cell] = crtPlayer;
win = check(grid, cell);
if(win)
break; /* Il y a un gagnant ! */
/* On passe au joueur suivant
* 3 - 1 = 2
* 3 - 2 = 1
*/
crtPlayer = 3 - crtPlayer;
}
/* Fin de partie */
Grid_display(grid);
if(win)
sprintf(buf, "Le joueur %d gagne !\n", crtPlayer);
else
sprintf(buf, "Match Nul !");
IO_displayMsg(buf);
}
/* ---------------------------------------------------------------------------*/
int main(void)
{
int done = 0;
srand(time(NULL));
do
{
play();
done = IO_ask();
}while(!done);
return EXIT_SUCCESS;
}
edit: code modifié.
J'ai séparé complètement la logique du jeu de l'affichage. et corrigé une étourderie en passant.
edit2: correction du problème signalé par @che plus bas.
× 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.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles - ♡ Copying is an act of love.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles - ♡ Copying is an act of love.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles - ♡ Copying is an act of love.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles - ♡ Copying is an act of love.
🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles - ♡ Copying is an act of love.