Advertisement
Guest User

Untitled

a guest
Jun 29th, 2013
406
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.29 KB | None | 0 0
  1. /*
  2. ** Nom: ******
  3. ** Prénom: Jérôme
  4. ** Groupe: 2103
  5. ** Description: Dossier LC10 - Puissance 4 (Joueur contre ordinateur)
  6. ** Dernière modification: 04/02/2013
  7. */
  8.  
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <time.h>
  13.  
  14. #define GAME_COLUMNS 7 /* (Max: 9) */
  15. #define GAME_LINES 6 /* (Max: 9) */
  16.  
  17. typedef enum
  18. {
  19.     /* Les affectations ici ne servent qu'à avoir un code compréhensible (Car l'énumération garanti ces valeurs) */
  20.     CS_Unoccupied      = 0, /* Cellule inocupée */
  21.     CS_OwnedByPlayer   = 1, /* Cellule possédée par le joueur */
  22.     CS_OwnedByComputer = 2  /* Cellule possédée par l'ordinateur */
  23. }
  24. CellState;
  25.  
  26. typedef struct
  27. {
  28.     CellState cells[GAME_LINES][GAME_COLUMNS];
  29.     unsigned int fullCellCount; /* Pour tester plus facilement le nombre de cellules pleines */
  30. }
  31. Game;
  32.  
  33. void ClearStdin();
  34. int Game_AI(Game* game);
  35. int Game_Aligned(Game* game, int line, int column, CellState state);
  36. int Game_GetColumnScore(Game* game, int column);
  37. int Game_GetFreeLine(Game* game, int column);
  38. int Game_Play(Game* game, int playerId, int column);
  39. int Game_PlayAI(Game* game, int playerId, int* column);
  40. void Game_Print(Game* game);
  41. void Game_Reset(Game* game);
  42. int ReadNumeral(int* numeral);
  43. int ReadYesNo();
  44.  
  45. int main()
  46. {
  47.     Game game;
  48.     int choice;
  49.     int newGame;
  50.     int playing;
  51.     int playerId = 0;
  52.     int line;
  53.  
  54.     printf("Puissance 4 - Version joueur contre ordinateur.\n");
  55.  
  56.     srand(time(NULL)); /* Un peu d'aléatoire pour diversifier l'IA dans ses choix */
  57.  
  58.     do
  59.     {
  60.         Game_Reset(&game);
  61.         Game_Print(&game);
  62.         playing = 1;
  63.         newGame = 0;
  64.  
  65.         do
  66.         {
  67.             if (playerId == 0)
  68.             {
  69.                 printf("Au tour du joueur !\n");
  70.  
  71.                 for (;;)
  72.                 {
  73.                     printf("Choix de colonne [1,%d] => ", GAME_COLUMNS);
  74.  
  75.                     if (!ReadNumeral(&choice) || choice < 1 ||choice > GAME_COLUMNS)
  76.                         continue;
  77.  
  78.                     choice--;
  79.  
  80.                     line = Game_Play(&game, playerId, choice);
  81.                     if (line == -1)
  82.                     {
  83.                         printf("Cette colonne est pleine !\n");
  84.                         continue;
  85.                     }
  86.  
  87.                     break;
  88.                 }
  89.             }
  90.             else
  91.             {
  92.                 printf("Au tour de l'ordinateur !\n");
  93.                 choice = Game_AI(&game);
  94.                 printf("L'ordinateur choisit la colonne %d\n", choice+1);
  95.  
  96.                 line = Game_Play(&game, playerId, choice);
  97.             }
  98.  
  99.             Game_Print(&game);
  100.  
  101.             if (Game_Aligned(&game, line, choice, CS_Unoccupied) >= 4) /* Avons-nous quatre jetons alignés ? */
  102.             {
  103.                 playing = 0;
  104.                 printf("******** Le joueur %d a gagn%c ! ********\n", playerId+1, 130);
  105.                 printf("Voulez-vous rejouer ? (O/N) : ");
  106.                 newGame = ReadYesNo();
  107.             }
  108.             else if (game.fullCellCount == GAME_COLUMNS*GAME_LINES) /* Le jeu est-il plein ? */
  109.             {
  110.                 playing = 0;
  111.                 printf("******** Match nul ! ********\n");
  112.                 printf("Voulez-vous rejouer ? (O/N) : ");
  113.                 newGame = ReadYesNo();
  114.             }
  115.  
  116.             /* Exécuté même en cas de victoire, de sorte que le joueur perdant commence si la partie recommence */
  117.             playerId = (playerId+1) % 2;
  118.         }
  119.         while (playing);
  120.     }
  121.     while (newGame);
  122.  
  123.     return 0;
  124. }
  125.  
  126. /*
  127. Input: N/A
  128. Output: N/A
  129. Process: Vide le buffer stdin (S'assurer qu'il contienne quelque chose avant)
  130. */
  131. void ClearStdin()
  132. {
  133.     int c;
  134.     while ((c = getchar()) != '\n' && c != EOF); /* On vide stdin */
  135. }
  136.  
  137. /*
  138. Input: Pointeur vers structure du jeu
  139. Output: Indice de la colonne choisie par l'IA
  140. Process: Calcule l'indice de la colonne représentant le plus gros avantage
  141. */
  142. int Game_AI(Game* game)
  143. {
  144.     int highestScore = 0;
  145.     int highestScoreColumn = 0;
  146.     int i = 0;
  147.     int score;
  148.  
  149.     for (i = 0; i < GAME_COLUMNS; ++i)
  150.     {
  151.         /*
  152.         ** On donne un score à chaque colonne et on choisit celle ayant le plus gros score
  153.         ** Ce score sera de 0 pour les colonnes pleines, il est nécessaire qu'il y ait au moins une colonne pleine lors de l'appel
  154.         */
  155.         score = Game_GetColumnScore(game, i);
  156.  
  157.         /* Pour que l'IA ne joue pas toujours pareil, si deux colonnes ont le même score, le choix est aléatoire */
  158.         if (score > highestScore || (score == highestScore && rand()%2 == 0))
  159.         {
  160.             highestScore = score;
  161.             highestScoreColumn = i;
  162.         }
  163.     }
  164.  
  165.     return highestScoreColumn;
  166. }
  167.  
  168. /*
  169. Input: Pointeur vers structure du jeu, la ligne et la colonne de la case de référence, la référence
  170. (Note: Si la référence vaut CS_Unoccupied, celle de la case de référence est prise)
  171. Output: Le nombre de cases alignées maximum
  172. Process: Calcule combien de cases ayant le même status sont alignées
  173. */
  174. int Game_Aligned(Game* game, int line, int column, CellState state)
  175. {
  176.     int i, j;
  177.     int current;
  178.     int max = 0;
  179.     CellState* ptr;
  180.     CellState* limit;
  181.  
  182.     if (state == CS_Unoccupied)
  183.     {
  184.         state = game->cells[line][column];
  185.         if (state == CS_Unoccupied)
  186.             return 0;
  187.     }
  188.  
  189.     /* Vérification horizontale (-) */
  190.     current = 1;
  191.  
  192.     /* De gauche à droite */
  193.     limit = &game->cells[line][GAME_COLUMNS-1];
  194.     for (ptr = &game->cells[line][column+1]; ptr <= limit && *ptr == state; ++ptr)
  195.         current++;
  196.  
  197.     /* De droite à gauche */
  198.     limit = &game->cells[line][0];
  199.     for (ptr = &game->cells[line][column-1]; ptr >= limit && *ptr == state; --ptr)
  200.         current++;
  201.  
  202.     if (current > max)
  203.         max = current;
  204.  
  205.     /* Vérification verticale (|) */
  206.     current = 1;
  207.  
  208.     /* De bas en haut */
  209.     limit = &game->cells[0][column];
  210.     for (ptr = &game->cells[line-1][column]; ptr >= limit && *ptr == state; ptr -= GAME_COLUMNS)
  211.         current++;
  212.  
  213.     /* De haut en bas */
  214.     limit = &game->cells[GAME_LINES-1][column];
  215.     for (ptr = &game->cells[line+1][column]; ptr <= limit && *ptr == state; ptr += GAME_COLUMNS)
  216.         current++;
  217.  
  218.     if (current > max)
  219.         max = current;
  220.  
  221.     /* Vérification première diagonale (\) */
  222.     current = 1;
  223.  
  224.     /* Montant */
  225.  
  226.     /* On doit d'abord retrouver la case limite (Celle qui touche les bords */
  227.     i = line;
  228.     j = column;
  229.     while (i > 0 && j > 0)
  230.         i--, j--;
  231.  
  232.     limit = &game->cells[i][j];
  233.  
  234.     for (ptr = &game->cells[line-1][column-1]; ptr >= limit && *ptr == state; ptr -= GAME_COLUMNS+1)
  235.         current++;
  236.  
  237.     /* Descendant */
  238.     i = line;
  239.     j = column;
  240.     while (i < GAME_LINES-1 && j < GAME_COLUMNS-1)
  241.         i++, j++;
  242.  
  243.     limit = &game->cells[i][j];
  244.  
  245.     for (ptr = &game->cells[line+1][column+1]; ptr <= limit && *ptr == state; ptr += GAME_COLUMNS+1)
  246.         current++;
  247.  
  248.     if (current > max)
  249.         max = current;
  250.  
  251.     /* Vérification seconde diagonale (/) */
  252.     current = 1;
  253.  
  254.     /* Montant */
  255.     i = line;
  256.     j = column;
  257.     while (i > 0 && j < GAME_COLUMNS-1)
  258.         i--, j++;
  259.  
  260.     limit = &game->cells[i][j];
  261.  
  262.     for (ptr = &game->cells[line-1][column+1]; ptr >= limit && *ptr == state; ptr -= GAME_COLUMNS-1)
  263.         current++;
  264.  
  265.     /* Descendant */
  266.     i = line;
  267.     j = column;
  268.     while (i < GAME_LINES-1 && j > 0)
  269.         i++, j--;
  270.  
  271.     limit = &game->cells[i][j];
  272.  
  273.     for (ptr = &game->cells[line+1][column-1]; ptr <= limit && *ptr == state; ptr += GAME_COLUMNS-1)
  274.         current++;
  275.  
  276.     if (current > max)
  277.         max = current;
  278.  
  279.     return max;
  280. }
  281.  
  282. /*
  283. Input: Pointeur vers structure du jeu, la colonne
  284. Output: Le score attribué à cette colonne
  285. Process: Calcule un score pour la colonne selon différents facteurs
  286. */
  287. int Game_GetColumnScore(Game* game, int column)
  288. {
  289.     /*
  290.     ** Le coeur de l'IA se situe ici
  291.     ** Note: Elle est améliorable; elle essaye de former des suites
  292.     ** sans vérifier qu'elle possède assez de place pour en placer 4
  293.     */
  294.     int line = Game_GetFreeLine(game, column);
  295.     int computerAligned;
  296.     int playerAligned;
  297.     int topComputerAligned;
  298.     int topPlayerAligned;
  299.  
  300.     if (line == -1)
  301.         return 0; /* Cette colonne ne peut pas être jouée */
  302.  
  303.     /* Combien de jetons pouvons-nous aligner en jouant ici ? */
  304.     computerAligned = Game_Aligned(game, line, column, CS_OwnedByComputer);
  305.  
  306.     if (computerAligned >= 4)
  307.         return computerAligned*100; /* On peut gagner en jouant ici */
  308.     else
  309.     {
  310.         /* Combien de jetons le joueur peut-il aligner en jouant ici ? */
  311.         playerAligned = Game_Aligned(game, line, column, CS_OwnedByPlayer);
  312.         if (playerAligned >= 4)
  313.             return playerAligned*50; /* Le joueur peut gagner en jouant ici, bloquons-le */
  314.         else
  315.         {
  316.             if (line > 0)
  317.             {
  318.                 /* Le troisième paramètre est la possibilité de jeu du joueur après notre tour */
  319.                 topPlayerAligned = Game_Aligned(game, line-1, column, CS_OwnedByPlayer);
  320.                 if (topPlayerAligned >= 4)
  321.                     return 1; /* Jouer ici nous ferait perdre le tour d'après (0 est la valeur pour les colonnes impossibles, donc 1) */
  322.  
  323.                 /* Le quatrième paramètre est la possibilité de jeu de l'ordinateur après notre tour */
  324.                 topComputerAligned = Game_Aligned(game, line-1, column, CS_OwnedByPlayer);
  325.             }
  326.             else
  327.             {
  328.                 topComputerAligned = 0;
  329.                 topPlayerAligned = 0;
  330.             }
  331.  
  332.             /*
  333.             ** On évalue la situation en prenant en compte ces quatre paramètres et en leur donnant un poids dans la décision
  334.             ** Ces valeurs changent totalement la façon de jouer de l'IA, je pense les avoir suffisamment équilibrées pour
  335.             ** que l'IA donne un minimum de challenge
  336.             */
  337.  
  338.             return computerAligned*14 + playerAligned*17 + topComputerAligned*4 - topPlayerAligned*6;
  339.  
  340.             /*
  341.             Autre set de valeurs donnant de bons résultats:
  342.             return computerAligned*10 + playerAligned*18 - topComputerAligned*3 - topPlayerAligned*4;
  343.             */
  344.         }
  345.     }
  346. }
  347.  
  348. /*
  349. Input: Pointeur vers structure du jeu, la ligne, la colonne
  350. Output: Le numéro de la première ligne libre en partant du bas ou -1 si colonne pleine
  351. Process: Parcourt les lignes de la colonne de bas en haut pour retrouver la première libre
  352. */
  353. int Game_GetFreeLine(Game* game, int column)
  354. {
  355.     int i;
  356.  
  357.     CellState* ptr = &game->cells[GAME_LINES-1][column]; /* Pour avoir un pointeur */
  358.     for (i = GAME_LINES-1; i >= 0; --i)
  359.     {
  360.         if (*ptr == CS_Unoccupied)
  361.             return i;
  362.  
  363.         ptr -= GAME_COLUMNS;
  364.     }
  365.  
  366.     return -1;
  367. }
  368.  
  369. /*
  370. Input: Pointeur vers structure du jeu, l'identifiant du joueur et la colonne
  371. Output: La ligne où le pion a été placé (-1 si colonne pleine)
  372. Process: Insère un pion appartenant à un certain joueur dans la colonne donnée.
  373. */
  374. int Game_Play(Game* game, int playerId, int column)
  375. {
  376.     int line = Game_GetFreeLine(game, column);
  377.  
  378.     if (line != -1)
  379.     {
  380.         game->cells[line][column] = (CS_Unoccupied + playerId + 1);
  381.         game->fullCellCount++;
  382.     }
  383.  
  384.     return line;
  385. }
  386.  
  387. /*
  388. Input: Pointeur vers structure du jeu
  389. Output: N/A
  390. Process: Affiche la grille de jeu
  391. */
  392. void Game_Print(Game* game)
  393. {
  394.     char CellStateToChar[] =
  395.     {
  396.         ' ', /* CS_Unoccupied */
  397.         'X', /* CS_OwnedByPlayer */
  398.         'O'  /* CS_OwnedByComputer */
  399.     };
  400.     int i, j;
  401.     CellState* cell = &game->cells[0][0]; /* Pour utiliser un pointeur */
  402.  
  403.     putchar('\n');
  404.  
  405.     for (i = 0; i < GAME_LINES; ++i)
  406.     {
  407.         printf("\t|");
  408.         for (j = 0; j < GAME_COLUMNS; ++j)
  409.             printf(" %c |", CellStateToChar[*cell++]);
  410.  
  411.         putchar('\n');
  412.     }
  413.  
  414.     /* Ligne de séparation */
  415.     printf("\t-");
  416.     for (j = 0; j < GAME_COLUMNS; ++j)
  417.         printf("----");
  418.  
  419.     putchar('\n');
  420.  
  421.     /* Numéros de colonne */
  422.     printf("\t|");
  423.     for (j = 1; j <= GAME_COLUMNS; ++j)
  424.         printf(" %hd |", j);
  425.  
  426.     puts("\n\n");
  427. }
  428.  
  429. /*
  430. Input: Pointeur vers structure du jeu
  431. Output: N/A
  432. Process: Fait repartir le jeu de zéro en vidant la grille
  433. */
  434. void Game_Reset(Game* game)
  435. {
  436.     memset(game->cells, CS_Unoccupied, GAME_COLUMNS*GAME_LINES*sizeof(CellState));
  437.     game->fullCellCount = 0;
  438. }
  439.  
  440. /*
  441. Input: Pointeur vers le chiffre
  442. Output: 1 en cas de succès et 0 en cas d'échec
  443. Process: Lit un chiffre seul depuis stdin, tout en prenant soin de vider ce dernier
  444. */
  445. int ReadNumeral(int* numeral)
  446. {
  447.     int c;
  448.  
  449.     c = getchar();
  450.     if (c >= '0' && c <= '9') /* Le caractère est-il un chiffre ? */
  451.     {
  452.         *numeral = c - '0'; /* Sortie du chiffre */
  453.  
  454.         /*
  455.         ** Est-ce que c'était tout ce que contenait stdin ?
  456.         ** Cet appel permet également de vider le buffer
  457.         */
  458.         c = getchar();
  459.         if (c == '\n')
  460.             return 1; /* Oui, l'utilisateur a bien entré un chiffre */
  461.         else /* Non, il reste encore quelque chose dans stdin */
  462.         {
  463.             ClearStdin(); /* On vide stdin */
  464.             return 0; /* Il y avait d'autres caractères, ce n'était pas un simple chiffre */
  465.         }
  466.     }
  467.     else /* Pas un chiffre */
  468.     {
  469.         if (c != '\n') /* L'utilisateur a-t-il entré quelque chose ? */
  470.             ClearStdin(); /* Non, nous vidons stdin */
  471.  
  472.         return 0; /* Pas un chiffre */
  473.     }
  474. }
  475.  
  476. /*
  477. Input: N/A
  478. Output: 1 si oui, 0 si non
  479. Process: Lit une confirmation de l'utilisateur (Basée sur la première lettre: O/Y pour oui, autre pour non)
  480. */
  481. int ReadYesNo()
  482. {
  483.     int c;
  484.  
  485.     c = getchar();
  486.     if (c == 'O' || c == 'o' || c == 'Y' || c == 'y')
  487.     {
  488.         ClearStdin();
  489.         return 1;
  490.     }
  491.     else if (c != '\n')
  492.         ClearStdin();
  493.  
  494.     return 0;
  495. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement