/*
 * Copyright (c) 2006 RAPHAEL CHAMPEIMONT and NABIL BEN HARIZ
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* Version du 29 novembre 2006 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* macros pour les couleurs en escape ANSI */
#define COULEUR_NORMALE "[0;1;40;37m" /* blanc */
#define COULEUR_GRILLE "[0;40;37m" /* gris clair */

/* remarque : les joueurs sont apelles 0 et 1 dans le programme
   mais on affiche joueur 1 et 2 a l'ecran */
#define JOUEURS_MAX 6

/* largeur et hauteur maximales de la grille de jeu */
#define LARGEUR_MAX 39
#define HAUTEUR_MAX 7

/* largeur et hauteur effectives de la grille */
int largeur = 7;
int hauteur = 6;

/* VARIABLES UTILISEES POUR L'AFFICHAGE EN COULEUR */
/* vaut 1 si on utilise de la couleur pour l'affichage, 0 sinon */
int couleur = 0;
/* chaines d'escapes ANSI pour la couleur des joueurs
   12 est la taille necessaire pour stocker la sequence */
char couleurJoueur[JOUEURS_MAX][12];

/* une case contient -1 si la case de la grille de jeu est vide
   0 si elle contient un jeton du joueur 0
   1 si elle contient un jeton du joueur 1
   la case grille[x][y] a ses coordonnees ayant le coin en haut a gauche
   comme origine */
int grille[LARGEUR_MAX][HAUTEUR_MAX];

/* vrai si l'utilisateur veut garder la meme configuration que
   dans la partie precedante */
int memeConfig = 0;

/* chaines contenant le nom des joueurs */
char noms[JOUEURS_MAX][31];

/* char contenant le signe (X ou O par exemple) du joueur */
char signeJoueur[JOUEURS_MAX];

/* contient le type du joueur, 0 = humain, 1 = AI */
int typeJoueur[JOUEURS_MAX];

/* numero du tour que l'on joue */
unsigned int tour = 0;

/* nombre de joueur */
int joueurs = 0;

/* coordonnees du dernier jeton joue */
int dernierJetonC;
int dernierJetonL;

/* mode debutant ? */
int debutant = 0;
const char signeCoupGagnant = '.';

/* message de l'AI */
char message[40];

/* FONCTIONS */

/* fonction qui saute l lignes */
void sauterLignes(unsigned int l) {
  int i;
  for (i=0; i<l; i++) {
    printf("\n");
  }
}

/* fonction qui affiche un message de bienvenue */
void bienvenue() {
  sauterLignes(5);
  printf("Bienvenue dans le jeu de puissance 4 fait par \nNabil Ben Hariz et Raphael Champeimont.\n");
  sauterLignes(1);
}

/* demande un choix oui/non a l'utilisateur et retourne la reponse */
int choix() {
  char c;
  int reponse = -1;
  while (reponse == -1) {
    printf("\nEntrez o (oui) ou n (non) : ");
    c = getchar();
    while (getchar() != '\n' && !feof(stdin)); /* si EOF on ne "bloque" pas */
    if (feof(stdin)) {
      clearerr(stdin); /* en cas de EOF on l'enleve */
    }
    switch (c) {
    case 'o':
    case 'O':
    case 'y':
    case 'Y':
      reponse = 1;
      break;
    case 'n':
    case 'N':
      reponse = 0;
      break;
    }
  }
  return reponse;
}

/* fonction de test verifiant si le terminal de l'utilisateur supporte
   la couleur */
void demanderSiCouleur() {
  printf("                                                              \033[0;1;31;40m\nTEST                                                          \033[0m\n\nLe mot \"TEST\" ci-dessus est-il ecrit en rouge ?\n\n(ce test permet de verifier si votre terminal supporte la couleur)\n\n");
  /* on assigne 1 ou 0 a couleur selon le choix de l'utilisateur */
  couleur = choix();
}

/* demande le nombre de joueurs */
void demanderNombreJoueurs() {
  while (1) {
    printf("Veuillez entrer le nombre de joueurs (2 a %d) :\n", JOUEURS_MAX);
    scanf("%d", &joueurs);
    /* vider le buffer -> evite un bug au cas ou l'utilisateur
       entre autre chose qu'un nombre */
    while (getchar() != '\n' && !feof(stdin));
    if (feof(stdin)) {
      clearerr(stdin); /* en cas de EOF on l'enleve */
    }
    /* on retourne si le nombre de joueurs est valide */
    if (joueurs >= 2 && joueurs <= JOUEURS_MAX) {
      return;
    }
    printf("-> Nombre de joueurs invalide.\n");
  }
}

/* demande la taille de la grille */
void demanderTailleGrille() {
  while (1) {
    printf("Veuillez entrer la largeur de la grille, par exemple 7, maximum %d :\n", LARGEUR_MAX);
    scanf("%d", &largeur);
    /* vider le buffer -> evite un bug au cas ou l'utilisateur
       entre autre chose qu'un nombre */
    while (getchar() != '\n' && !feof(stdin));
    if (feof(stdin)) {
      clearerr(stdin); /* en cas de EOF on l'enleve */
    }
    /* on sort de la boucle si le nombre entre est valide */
    if (largeur > 0 && largeur <= LARGEUR_MAX) {
      break;
    }
    printf("-> Largeur invalide.\n");
  }
  printf("\n");
  while (1) {
    printf("Veuillez entrer la hauteur de la grille, par exemple 6, maximum %d :\n", HAUTEUR_MAX);
    scanf("%d", &hauteur);
    /* vider le buffer -> evite un bug au cas ou l'utilisateur
       entre autre chose qu'un nombre */
    while (getchar() != '\n' && !feof(stdin));
    if (feof(stdin)) {
      clearerr(stdin); /* en cas de EOF on l'enleve */
    }
    /* on sort de la boucle si le nombre entre est valide */
    if (hauteur > 0 && hauteur <= HAUTEUR_MAX) {
      break;
    }
    printf("-> Hauteur invalide.\n");
  }
}


/* demande la couleur d'un joueur */
void saisirCouleur(int joueur) {
  int i;
  int couleurLue;
  /* si on ne veut pas de couleur, retourner directement */
  if (!couleur) {
    return;
  }
  printf("\033%s", COULEUR_NORMALE);
  while (1) {
    if (typeJoueur[joueur] == 0) { /* si le joueur est humain */
      printf("Joueur %d : Veuillez choisir votre couleur (entrez le numero) :\n          Couleurs : ", joueur+1);
    } else {
      printf("Veuillez choisir la couleur du joueur %d (entrez le numero) :\n          Couleurs : ", joueur+1);
    }
    for (i=1; i<=6; i++) {
      printf("\033[0;1;40;3%dm%d ", i, i);
    }
    printf("\033%s\n", COULEUR_NORMALE);
    scanf("%d", &couleurLue);
    snprintf(couleurJoueur[joueur], sizeof(couleurJoueur[joueur]), "[0;1;40;3%dm", couleurLue);
    /* vider le buffer -> evite un bug au cas ou l'utilisateur
       entre autre chose qu'un nombre */
    while (getchar() != '\n' && !feof(stdin));
    if (feof(stdin)) {
      clearerr(stdin); /* en cas de EOF on l'enleve */
    }
    /* on retourne si la couleur est valide */
    if (couleurLue >= 1 && couleurLue <= 6) {
      return;
    }
    printf("-> Couleur invalide.\n");
  }
}



/* demande a entrer le nom du joueur */
void saisirNom(int joueur) {
  int i = 0;
  char c;
  while (1) {
    if (couleur) {
      printf("\033%s", couleurJoueur[joueur]);
    }
    printf("Joueur %d : Veuillez entrez votre nom (%lu caracteres max) :\n", joueur+1, (long unsigned int)sizeof(noms[joueur])-1);
    c = getchar();
    while (c != '\n' && !feof(stdin)) {
      /* si on est avant le dernier char de la chaine */
      if (i < sizeof(noms[joueur])-1) {
	noms[joueur][i] = c;
      } else if (i == sizeof(noms[joueur])-1) { /* on en est au dernier char */
	noms[joueur][i] = '\0';
      } /* si on a depasse le dernier char, on ne fait rien */
      c = getchar();
      i++;
    }
    if (feof(stdin)) {
      clearerr(stdin); /* en cas de EOF on l'enleve */
    }
    /* si la chaine entre est plus courte que la taille (maximale) de
       notre chaine, alors on met en '\0' apres le dernier caractre ecrit */
    if (i < sizeof(noms[joueur])) {
      noms[joueur][i] = '\0';
    } else {
      printf("Attention : votre nom sera coupe car il est trop long.\n");
    }
    if (couleur) {
      printf("\033%s", COULEUR_NORMALE);
    }
    /* si la taille du nom est non nulle, on retourne
       sinon (si l'utilisateur a appuye sur ENTREE sans rien taper)
       on demande a nouveau le nom */
    if (strlen(noms[joueur]) != 0) {
      break;
    }
  }
}

/* fonction qui demande a entrer le signe du joueur */
void saisirSigne(int joueur) {
  int i;
  int continuer = 1;
  while (continuer) {
    continuer = 0;
    if (couleur) {
      printf("\033%s", couleurJoueur[joueur]);
    }
    if (typeJoueur[joueur] == 0) {
      printf("%s : Veuillez entrez votre signe\n(le caractere qui representera votre jeton) :\n", noms[joueur]);
    } else {
      printf("%s : Veuillez entrez le signe utilise par ce joueur AI\n(le caractere qui representera ses jetons) :\n", noms[joueur]);
    }
    signeJoueur[joueur] = getchar();
    while (getchar() != '\n' && !feof(stdin));
    if (feof(stdin)) {
      clearerr(stdin); /* en cas de EOF on l'enleve */
    }
    if (couleur) {
      printf("\033%s", COULEUR_NORMALE);
    }
    /* si le signe n'est pas valide on recommence */
    if (signeJoueur[joueur] < 33 || signeJoueur[joueur] > 126 || signeJoueur[joueur] == signeCoupGagnant) {
      continuer = 1;
      printf("\n==> Erreur : signe non autorise.\n\n");
      continue; /* on recommence sans aller jusqu'ou bout de la boucle */
    }
    /* on verifie qu'un autre joueur n'a pas deja choisi ce signe */
    for (i=0; i<joueur; i++) {
      if (signeJoueur[i] == signeJoueur[joueur]) {
	continuer = 1;
	printf("\n==> Erreur : signe deje choisi.\n\n");
	break;
      }
    }
  }
}


/* affiche une ligne du type +-+-+-+-+-+ pour la grille de jeu */
void afficherLigneDeGrille() {
  int i;
  /* si on utilise la couleur, mettre la couleur de la grille */
  if (couleur) {
    printf("\033%s", COULEUR_GRILLE);
  }
  /* afficher la ligne */
  for (i=0; i<largeur; i++) {
    printf("+-");
  }
  printf("+\n");
  /* si on utilise la couleur, mettre la couleur normale */
  if (couleur) {
    printf("\033%s", COULEUR_NORMALE);
  }
}

/* retourne 1 si on peut mettre un jeton dans la colonne x */
int jouable(int x) {
  /* retourne vrai si x est dans [0;largeur[ et 
     que la derniere case (la + haute) de la colonne est vide */
  return (x >= 0 && x < largeur && grille[x][0] == -1);
}

/* retourne 1 si la case (x,y) est dans la grille, sinon 0 */
int dansGrille(int x, int y) {
  return (x >= 0 && y >= 0 && x < largeur && y < hauteur);
}

/* fonction qui a partir d'une position (c,l) d'un jeton d'un joueur
   retourne le nombre de jetons appartenant a ce joueur dans la direction
   (dirV, dirH) */
int adjacentJoueur(int c, int l, int dirH, int dirV, int proprietaire) {
  int resultat = 0;
  while(1) {
    c=c+dirH;
    l=l+dirV;
    /* si (c,l) n'est pas dans la grille */
    if (!dansGrille(c,l)) {
      return resultat; /* on aura pas plus de jetons adjacents */
    }
    if (grille[c][l] == proprietaire) {
      resultat++; /* on a un jeton en plus adjacent */
    } else {
      return resultat; /* on aura pas plus de jetons adjacents */
    }
  }
}

/* cette fonction regarde les jetons adjacents a un jeton (c,l)
   qui doit exister */
int adjacent(int c, int l, int dirH, int dirV) {
  if (grille[c][l] < 0) {
    printf("Erreur : il n'y a pas de jeton dans la case (%d,%d).\n", c, l);
    exit(1);
  }
  return adjacentJoueur(c, l, dirH, dirV, grille[c][l]);
}

/* fonction qui retourne vrai si le jeton (c,l) fait partie
   d'une serie de 4 */
int partieEstGagneeOuPas(int c, int l) {
  /* on verifie s'il est dans une serie de 4 jetons (ou +)
     horizontale, verticale, oblique ou oblique dans l'autre sens */
  if (grille[c][l] < 0) {
    printf("Erreur : case vide.\n");
    exit(1);
  }
  return ( (1+adjacent(c,l,  0,  1)+adjacent(c,l,  0, -1) >= 4)
	   || (1+adjacent(c,l,  1,  0)+adjacent(c,l, -1 , 0) >= 4)
	   || (1+adjacent(c,l,  1,  1)+adjacent(c,l, -1, -1) >= 4)
	   || (1+adjacent(c,l,  1, -1)+adjacent(c,l, -1,  1) >= 4) );
}

/* fonction qui retourne vrai si le joueur J gagne s'il met un jeton
   dans la colonne c ligne l */
int partieEstGagneeOuPasSiJoue(int c, int l, int J) {
  if (grille[c][l] >= 0) {
    printf("Erreur : case deja pleine.\n");
    exit(1);
  }
  /* on verifie s'il est dans une serie de 4 jetons (ou +)
     horizontale, verticale, oblique ou oblique dans l'autre sens */
  return (
   (1+adjacentJoueur(c,l,  0,  1, J)+adjacentJoueur(c,l,  0, -1, J) >= 4)
   || (1+adjacentJoueur(c,l,  1,  0, J)+adjacentJoueur(c,l, -1 , 0, J) >= 4)
   || (1+adjacentJoueur(c,l,  1,  1, J)+adjacentJoueur(c,l, -1, -1, J) >= 4)
   || (1+adjacentJoueur(c,l,  1, -1, J)+adjacentJoueur(c,l, -1,  1, J) >= 4) );
}

/* fonction qui retourne quel joueur peut gagner s'il met un jeton
   dans la case (c, l),
   elle retroune -1 si personne ne peut gagner */
int quiPeutGagnerIci(int c, int l) {
  int j;
  for (j=0; j<joueurs; j++) {
    if (partieEstGagneeOuPasSiJoue(c, l, j)) {
      return j;
    }
  }
  return -1;
}

/* retourne la ligne ou arrive un jeton si on le met dans la colonne c */
int arrive(int c) {
  int l;
  for (l=hauteur-1; l>=0; l--) {
    if (grille[c][l] < 0) {
      return l;
    }
  }
  /* normalement on ne devrait pas arriver la puisqu'on a du trouver
     la place de mettre le jeton */
  printf("Erreur : n'a pas pu trouver la ligne.\n");
  exit(1);
}

/* affiche la grille de jeu */
void afficherGrille() {
  int j;
  int i;
  int possibleGagnant;
  /* affichage des numeros de colonnes */
  /* les dizaines */
  for (i=0; i<largeur; i++) {
    if (i<10) {
      printf("  ");
    } else {
      printf(" %d", i/10);
    }
  }
  printf("\n");
  /* les chiffres des unites */
  for (i=0; i<largeur; i++) {
    printf(" %d", i%10);
  }
  printf("\n");
  for (j=0; j<hauteur; j++) {
    afficherLigneDeGrille(); /* afficha d'un ligne de +-+-+-+ */
    for (i=0; i<largeur; i++) {
      /* on affcihe le signe  |  */
      if (!couleur) {
	printf("|");
      } else {
	printf("\033%s|\033%s", COULEUR_GRILLE, COULEUR_NORMALE);
      }
      /* on affiche le contenu de la case */
      if (grille[i][j] == -1) { /* la case est vide */
	if (!debutant) { /* en mode non debutant on affiche un espace */
	  printf(" ");
	} else {
	  /* mode dubutant : 
	     le coup est-il gagnant si on met un jeton dans cette case ? */
	  possibleGagnant = quiPeutGagnerIci(i, j);
	  if (possibleGagnant < 0) {
	    printf(" "); /* non */
	  } else { /* oui */
	    if (!couleur) {
	      printf("%c", signeCoupGagnant);
	    } else { /* si couleur, on affiche le ! dans le couleur
			du joueur qui peut gagner */
	      printf("\033%s%c\033%s", couleurJoueur[possibleGagnant], signeCoupGagnant, COULEUR_NORMALE);
	    }
	  }
	}
      } else {
	/* sans couleur */
	if (!couleur) {
	  printf("%c", signeJoueur[grille[i][j]]);
	  /* avec couleur */
	} else {
	  printf("\033%s%c\033%s", couleurJoueur[grille[i][j]], signeJoueur[grille[i][j]], COULEUR_NORMALE);
	}
      }
    }
    /* on affiche le signe | */
    if (!couleur) {
      printf("|");
    } else {
      printf("\033%s|\033%s", COULEUR_GRILLE, COULEUR_NORMALE);
    }
    /* passer a la ligne suivante */
    printf("\n");
  }
  afficherLigneDeGrille();
}

/* demande a l'utilisateur de choisir la colonne ou il veut jouer
   jusqu'a ce que la colonne soit valide (qu'on puisse y mettre un jeton)
   puis elle retrourne la colonne ou il a choisi de jouer */
int saisirCoup(int joueur) {
  int colonne;
  sauterLignes(2);
  if (debutant) {
    printf("Le signe %c montre les cases ou quelqu'un peut gagner en mettant un jeton.\n", signeCoupGagnant);
  } else {
    printf("\n");
  }
  while (1) {
    if (!couleur) {
      printf("%s : Entrez la colonne ou vous voulez jouer :\n", noms[joueur]);
    } else {
      printf("\033%s%s : Entrez la colonne ou vous voulez jouer :\033%s\n", couleurJoueur[joueur], noms[joueur], COULEUR_NORMALE);
    }
    scanf("%d", &colonne);
    /* vider le buffer -> evite un bug au cas ou l'utilisateur
       entre autre chose qu'un nombre */
    while (getchar() != '\n' && !feof(stdin));
    if (feof(stdin)) {
      clearerr(stdin); /* en cas de EOF on l'enleve */
    }
    if (jouable(colonne)) {
      return colonne;
    }
    sauterLignes(7);
    afficherGrille();
    sauterLignes(1);
    printf("*** Impossible de jouer dans cette colonne. ***\n");
    printf("==> Veuillez entrer a nouveau la colonne.\n");
  }
}

/* retourne la colonne ou l'ordinateur joue */
int AI(int joueur) {
  int c;
  int i;
  /* l'AI cherche si il peut gagner en mettant le jeton dans une colonne */
  for (c=0; c<largeur; c++) {
    if (jouable(c)) {
      if (partieEstGagneeOuPasSiJoue(c, arrive(c), joueur)) {
	snprintf(message, sizeof(message), "Je vais gagner !");
	return c;
      }
    }
  }
  /* l'AI empeche un autre joueur de gagner */
  for (c=0; c<largeur; c++) {
    if (jouable(c)) {
      if (quiPeutGagnerIci(c, arrive(c)) >= 0) {
	snprintf(message, sizeof(message), "Je ne laisserais pas un autre gagner !");
	return c;
      }
    }
  }
  /* sinon il cherche un colonne au hasard (largeur essais)  */
  for (i=0; i<largeur; i++) {
    c = rand()%largeur;
    if (jouable(c)) {
      snprintf(message, sizeof(message), "Au hasard !");
      return c;
    }
  }
  /* sinon il joue dans la premire colonne ou c'est possible */
  for (c=0; c<largeur; c++) {
    if (jouable(c)) {
      snprintf(message, sizeof(message), "La premiere que j'ai trouvee !");
      return c;
    }
  }
  printf("Erreur : AI : Je ne peux pas jouer !\n");
  exit(1);
}

/* (re-)initialiser la grille */
void initialiserGrille() {
  int i, j;
  for (i=0; i<largeur; i++) {
    for (j=0; j<hauteur; j++) {
      grille[i][j] = -1;
    }
  }
}

/* retourne 1 si la grille est pleine, sinon 0 */
int estRemplieOuNon() {
  int i;
  for (i=0; i<largeur; i++) {
    if (grille[i][0] == -1) {
      return 0;
    }
  }
  return 1;
}


/* fonction apellee comme on insere un jeton, elle met a jour le tableau
   grille et les variables dernierJeton */
void insereJeton(int joueur, int colonne) {
  int j;
  /* on cherche ou le jeton va arriver (a quelle ligne) */
  for (j=hauteur-1; j>=0; j--) {
    if (grille[colonne][j] < 0) {
      dernierJetonC = colonne;
      dernierJetonL = j;
      grille[colonne][j] = joueur;
      return;
    }
  }
  /* normalement on ne devrait pas arriver la puisqu'on a du trouver
     la place de mettre le jeton */
  printf("Erreur : n'a pas pu trouver de case libre.\n");
  exit(1);
}

int main() {
  int i;
  int joue;
  bienvenue();
  /* on initialise la graine aleatoire */
  srand(time(NULL));
  /* on demande si on veut de la couleur */
  demanderSiCouleur();
  /* changer la couleur du texte vers COULEUR_NORMALE */
  if (couleur) {
    printf("\033%s", COULEUR_NORMALE);
  }
  sauterLignes(3);

  /* boucle permettant de jouer plusieurs parties */
  while (1) {
    /* on demande les reglages a nouveau si memeConfig faux */
    if (!memeConfig) {
      /* on demande si on active le mode debutant */
      printf("\n\nActiver le mode debutant (affichage des coups gagnants) ?\n");
      debutant = choix();
      sauterLignes(3);
      /* on demande le nombre de joueurs */
      demanderNombreJoueurs();
      sauterLignes(3);
      /* demander la taille de la grille */
      demanderTailleGrille();
    }
    /* on remet la grille a zero */
    initialiserGrille();
    if (!memeConfig) {
      /* on demande le type, le nom, le signe et la couleur des joueurs */
      for (i=0; i<joueurs; i++) {
	sauterLignes(3);
	/* humain ou ordinateur ? */
	printf("Le joueur %d doit-il etre joue par l'ordinateur (AI) ?\n", i+1);
	typeJoueur[i] = choix();
	printf("\n");
	/* si la couleur est activee, on demande la couleur */
	if (couleur) {
	  saisirCouleur(i);
	  printf("\n");
	}
	/* si le joueur est reel, on lui demande son nom */
	if (typeJoueur[i] == 0) {
	  saisirNom(i);
	} else {
	  /* si le joueur est joue par l'ordinateur, on lui choisit un nom */
	  snprintf(noms[i], sizeof(noms[i]), "AI (joueur %d)", i+1);
	}
	sauterLignes(1);
	saisirSigne(i);
      }
    }
    /* on affiche la grille une premiere fois */
    sauterLignes(2);
    afficherGrille();
    /* boucle permettant de jouer plusieurs tours, il n'y a pas de condition
       car on peut jouer autant de tours de l'on veut (le break arrete la
       boucle) */
    for (tour=0;;tour++) {
      /* on calcule le joueur qui joue ce tour */
      joue = tour%joueurs;
      /* le joueur (tour%joueurs) joue -> donc 0, 1, 2, 3 ... */
      /* si le joueur est humain, on lui demande de saisir son coup */
      if (typeJoueur[joue] == 0) {
	insereJeton(joue, saisirCoup(joue));
      } else {
	/* si c'est l'ordinateur qui joue, on apelle la fonction AI() */
	insereJeton(joue, AI(joue));
      }
      /* on affiche ou le dernier joueur a joue */
      sauterLignes(20);
      if (typeJoueur[joue] == 0) {
	printf("%s joue dans la colonne %d.\n", noms[joue], dernierJetonC);
      } else {
	/* si c'est l'AI qui a joue on affiche son message */
	printf("%s joue dans la colonne %d. \"%s\"\n", noms[joue], dernierJetonC, message);
      }
      /* on affiche la grille */
      afficherGrille();
      /* on verifie si le joueur qui vient de jouer a gagne */
      if (partieEstGagneeOuPas(dernierJetonC, dernierJetonL)) {
	sauterLignes(1);
	if (!couleur) {
	  printf("=====> %s a gagne ! <=====\n", noms[joue]);
	} else {
	  printf("\033%s=====> %s a gagne ! <=====\n\033%s", couleurJoueur[joue], noms[joue], COULEUR_NORMALE);
	}
	sauterLignes(1);
	break;
      }
      /* si la grille est pleine et qu'il n'y a pas de gagnant -> ex aequo */
      if (estRemplieOuNon() == 1) {
	printf("=> EX AEQUO : La grille est pleine ! <=\n");
	break;
      }
    }
    /* fin de partie */
    /* on demande a l'utilisateur s'il veut rejouer */
    printf("\aLa partie est finie. Rejouer ?\n");
    if(!choix()) {
      break; /* il a repondu non -> sortir de la boucle */
    }
    sauterLignes(3);
    /* demander si on veut garder la meme configuration */
    printf("Garder la meme configuration que la partie precedente ?\n(nombre de joueurs, noms, signes, couleurs...)\n");
    memeConfig = choix();
  }

  /* fin du jeu (l'utilisateur ne veut plus rejouer) */
  sauterLignes(3);
  /* on remet la couleur a la valeur par defaut */
  if (couleur == 1) {
    printf("\033[0m");
  }
  printf("Au revoir !\n");
  sauterLignes(1);

  return 0;
}
