/*
Télécharger la bibliothèque advio2 là :
http://jvstats.free.fr/temp/advio2.zip
*/

#include <advio2.h>
using namespace advio2;

enum team
{
  _ROUGE,
  _JAUNE,
  AUCUN
};

void AffPion(int x,int y,team t);
bool gagne(team t);
void FinJeu(bool gagne);
void ai();
void mouse_function(event_mouse evt,void* param);
bool Init();
bool fin;

team grille[3][3];              //Le contenu de la grille de jeu
int lx,ly;
team current_player;
int nb_pion=0;
team ai_team;
graphics *jeu;          //La surface du jeu

int main()
{
  event_keyboard keyb;
  jeu=new graphics(450,450);
  //On effectue quelques réglage préliminaire
  HideConsole();              //On masque la console créé automatiquement par windows
  jeu->SetCaption("Morpion");  //On règle le titre de la fenêtre de jeu
  jeu->SetCoord(0,3,0,3);      //On définit un système de coordonnées, pour gérer simplement la surface de jeu
  jeu->SetLineWidth(3);        //On définit l'épaisseur des traits lors du dessin
debut_jeu:
  if (!Init())
  {
    delete jeu;
    return 0;
  }

  //On dessine la surface de jeu->
  jeu->fLine(1,0,1,3);
  jeu->fLine(2,0,2,3);
  jeu->fLine(0,1,3,1);
  jeu->fLine(0,2,3,2);

  //On définit la fonction appelée lors d'événement de la souris.
  jeu->SetMouseCallBack(mouse_function,0);

  if (current_player==ai_team)
    ai();

  //Cette boucle sers à empêcher le programme de quitter pendant que l'on joue. Le jeu se ferme si on appuie sur la touche echap.
  do
  {
    keyb=jeu->WaitKeyboardInput();
    if (fin&&keyb.keycode!=27)
      goto debut_jeu;
  } while (keyb.keycode!=27);
  delete jeu;
  return 0;
}

//Cette fonction est appelé à chaque fois qu'un événement de la souris se produit.
//C'est globalement la fonction la plus importante du programme.
void mouse_function(event_mouse evt,void* param)
{
  int x,y;
  x=(int)evt.fx;
  y=(int)evt.fy;
  if (grille[x][y]!=AUCUN)
    return;
  switch (evt.action)
  {
    case MOVE:
      if (x!=lx||y!=ly)
      {
        AffPion(lx,ly,AUCUN);
        lx=x;
        ly=y;
        AffPion(lx,ly,current_player);
      }
      break;
    case UP:
      ly=lx=-1;
      nb_pion++;
      AffPion(x,y,current_player);
      grille[x][y]=current_player;
      if (gagne(current_player))
      {
        FinJeu(true);
        return;
      }
      if (nb_pion==9)
      {
        FinJeu(false);
        return;
      }
      current_player=(team)(1-(int)current_player);
      if (ai_team==current_player)
        ai();
      break;
  }
}

//Affiche un pion donné dans la couleur de son équipe. Si t==AUCUN, le pion est effacé.
void AffPion(int x,int y,team t)
{
  switch (t)
  {
    case AUCUN:
      jeu->SetBackGround(BLANC);
      jeu->SetForeGround(BLANC);
      break;
    case _ROUGE:
      jeu->SetBackGround(ROUGE);
      jeu->SetForeGround(NOIR);
      break;
    case _JAUNE:
      jeu->SetBackGround(JAUNE);
      jeu->SetForeGround(NOIR);
      break;
  }
  jeu->fCircle(x+0.5f,y+0.5f,0.4f,BLANK);
}

//Renvoie si la couleur t a ou non gagné
bool gagne(team t)
{
  int i,j,k,l,m,n;
  m=n=0;
  for (i=0;i<3;i++)
  {
    k=l=0;
    for (j=0;j<3;j++)
    {
      if (grille[i][j]==t)
        k++;
      if (grille[j][i]==t)
        l++;
    }
    if (grille[i][i]==t)
      m++;
    if (grille[i][2-i]==t)
      n++;
    if (k==3||l==3)
      return true;
  }
  if (m==3||n==3)
    return true;
  return false;
}

//Cette fonction est appelé à la fin du jeu pour afficher le résultat
void FinJeu(bool gagne)
{
  jeu->SetMouseCallBack();
  jeu->SetBackGround(BLANC);
  jeu->Locate(1,20);
  if (gagne)
  {
    if (current_player==_JAUNE)
      jeu->Print("Le joueur jaune a gagné.");
    else
      jeu->Print("Le joueur rouge a gagné.");
  }
  else
  {
    jeu->Print("Vous avez tous perdu.");
  }
  jeu->Locate(1,60);
  jeu->Print("Appuyer sur echap pour quitter, ou sur une autre touche pour recommencer.");
  fin=true;
}

//Choisi une position à jouer pour le joueur courant. L'IA est très très sommaire.
void ai()
{
  team autre=(team)(1-(int)current_player);
  event_mouse evt;
  evt.action=UP;
  int i,j;
  for (i=0;i<3;i++)
    for (j=0;j<3;j++)
      if (grille[i][j]==AUCUN)
      {
        grille[i][j]=current_player;
        if (gagne(current_player))
        {
          evt.fx=i+.2;
          evt.fy=j+.2;
          grille[i][j]=AUCUN;
          goto fin_ai;
        }
        grille[i][j]=AUCUN;
      }

  for (i=0;i<3;i++)
    for (j=0;j<3;j++)
      if (grille[i][j]==AUCUN)
      {
        grille[i][j]=autre;
        if (gagne(autre))
        {
          evt.fx=i+.2;
          evt.fy=j+.2;
          grille[i][j]=AUCUN;
          goto fin_ai;
        }
        grille[i][j]=AUCUN;
      }

  while (1)
  {
    couleur_s co(RandomColor());
    i=co.rvb.b%3;
    j=co.rvb.r%3;
    if (grille[i][j]==AUCUN)
    {
      evt.fx=i+.2;
      evt.fy=j+.2;
      goto fin_ai;
    }
  }

fin_ai:
  mouse_function(evt,0);
}

//Initialise les données du jeu et demande à l'utilisateur le mode dans lequel il veut jouer.
bool Init()
{
  int i,j;
  for (i=0;i<3;i++)
    for (j=0;j<3;j++)
      grille[i][j]=AUCUN;
  lx=ly=-1;
  current_player=_JAUNE;
  nb_pion=0;
  fin=false;

  jeu->cls();
  jeu->Locate(20,20);
  jeu->Print("Jouer à ");jeu->SetForeGround(BLEU);jeu->Print("1");jeu->SetForeGround(NOIR);
  jeu->Print(" ou ");jeu->SetForeGround(BLEU);jeu->Print("2");jeu->SetForeGround(NOIR);
  jeu->Print(" joueurs ?");

  int nb=0;
  event_keyboard evt;
  while (nb!='1'&&nb!='2'&&nb!=27)
  {
    evt=jeu->WaitKeyboardInput();
    nb=evt.keycode;
  }
  if (nb==27)
    return false;
  jeu->cls();
  if (nb==2)
  {
    ai_team=AUCUN;
  }
  else
  {
    couleur_s co=RandomColor();
    ai_team=(team)(co.rvb.b%2);
  }
  return true;
}

