#include <stdio.h>
#include <conio.h>

int   InKey       ( void );
void  Maschera    ( int  );
void  ValutaMossa ( int *, int, int, int *, int * );

/*--------------------------- T R I S ---------------------------------------
   Programmma per il gioco del Tris tra due giocatori scelti tra:
   -) Umano ( tramite keyboard )
   -) PC    ( tramite sub di valutazione mossa )

La tavola di gioco (una scacchiera 3x3) viene allocata in un vettore;
ogni casella della Tavola vale 0 se vuota, 1 se occupata dal giocatore 1,
2 se occupata dal giocatore 2.

La valutazione della mossa migliore in ogni fase del gioco viene affidata
ad una funzione ricorsiva, che deve essere in grado di valutare la mossa
perfetta.
---------------------------------------------------------------------------*/

void main ( void )
{ int  Giocatori,      // giocatori attivi:
                       // 0 : Umano - Umano
                       // 1 : Computer - Umano
                       // 2 : Umano - Computer
       Who,            // giocatore che deve muovere: 1(primo) o 2
       Tavola[10],     // scacchiera di gioco
       Nvuote,         // numero delle caselle vuote (con valore 0)
       DoveGioco,      // mossa da fare da parte di Who
       QuantoVale,     // valore della mossa (vista da Who)
       Finito,         // -1 = configurazione iniziale
                       //  0 = gioco in corso
                       //  1 = gioco finito (vinto 1)
                       //  2 = gioco finito (vinto 2)
                       //  3 = gioco finito in pareggio
                       // 27 = gioco finito per abbandono
       i;

  Maschera (-1); //------------------- Visualizza il campo di gioco
  Finito = -1;
  do
  { switch ( i=InKey() )
    { case  27 : Giocatori = 0; Finito = 27; break; // Abbandono
      case 315 : Giocatori = 0; Finito =  0; break; // Umano - Umano
      case 316 : Giocatori = 1; Finito =  0; break; // Computer - Umano
      case 317 : Giocatori = 2; Finito =  0; break; // Umano - Computer
    }
  }
  while (Finito < 0);

  for (i=0; i<10; Tavola[i++]=0 ); // pulizia scacchiera
  Maschera (Giocatori);
  Who = 1;
  Nvuote = 9;
  textcolor(2); gotoxy ( 2, 16); cprintf("9 Caselle Vuote");

  //=============================== ciclo delle mosse ======================
  while (!Finito)
  { textcolor(13+Who); gotoxy (32, 23);
    cprintf ("Muove il giocatore %d \n", Who);
    DoveGioco = -1;

    if (Giocatori == Who) // mossa decisa dalla funzione di valutazione
    { gotoxy (2, 18); textcolor(12); cprintf(" ATTENDERE ! ");
      ValutaMossa (Tavola, Nvuote, Who, &DoveGioco, &QuantoVale);
      textcolor(13+Who);
      gotoxy (2, 18); cprintf("\aMossa %c ", DoveGioco+48);
      if (QuantoVale < 0)      cprintf("perdente");
      else if (QuantoVale > 0) cprintf("vincente");
      else                     cprintf("pareggio");
    }
    else // mossa del giocatore umano tramite keyboard
    { if ( (i = InKey() ) == 27)
        Finito = 27;
      else if (i>=49 && i<=57 && Tavola[i-48]==0) // 123456789
        DoveGioco = i-48;
      else if (i==319) // F5 help mossa migliore
      { gotoxy (2, 18); textcolor(12); cprintf(" ATTENDERE ! ");
        ValutaMossa (Tavola, Nvuote, Who, &DoveGioco, &QuantoVale);
        textcolor(13+Who);
        gotoxy (2, 18); cprintf("\aMossa %c ", DoveGioco+48);
        if (QuantoVale < 0)      cprintf("perdente");
        else if (QuantoVale > 0) cprintf("vincente");
        else                     cprintf("pareggio");
        DoveGioco = -1;
      }
    }

    if (DoveGioco >= 0)
    { Tavola[DoveGioco] = Who; Nvuote --; // occupa la casella
      if ( Tavola[1] & Tavola[2] & Tavola[3] ||     // 1° rigo
           Tavola[4] & Tavola[5] & Tavola[6] ||     // 2° rigo
           Tavola[7] & Tavola[8] & Tavola[9] ||     // 3° rigo
           Tavola[1] & Tavola[4] & Tavola[7] ||     // 1° colonna
           Tavola[2] & Tavola[5] & Tavola[8] ||     // 2° colonna
           Tavola[3] & Tavola[6] & Tavola[9] ||     // 3° colonna
           Tavola[1] & Tavola[5] & Tavola[9] ||     // diagonale /
           Tavola[7] & Tavola[5] & Tavola[3] )      // diagonale \
                              Finito = Who;         // mossa vincente
      else if ( Nvuote == 0 ) Finito = 3;           // pareggio
      else                    Who ^= 3;             // passa all'avversario

      for (i=9; i; i--) // Visualizza le pedine sul campo di gioco
        if (Tavola[i]) gotoxy (49+((i-1)%3)*10, 16-((i-1)/3)*4 ),
          textcolor (16-Tavola[i]),
          cprintf ("%c", 97-Tavola[i]*9 );
          textcolor(2); gotoxy(2,16); cprintf("%2d Caselle Vuote",Nvuote);
    }
  }

  textbackground(0); textcolor(12); gotoxy (30, 23);
  switch (Finito)
  { case  1: cprintf (" Vittoria al giocatore  X    \n"); break;
    case  2: cprintf (" Vittoria al giocatore  O    \n"); break;
    case  3: cprintf (" Partita pareggiata          \n"); break;
    case 27: cprintf (" Conclusione per Abbandono   \n"); break;
  }

  while (getch()!= 27); // ESC per uscire dal gioco
}

/*===========================================================================
  Funzione per la valutazione della mossa migliore in un dato stato del gioco
  supponendo che ciascuno giochi 'al meglio':
  La mossa vincente vale maggiormente se fatta a inizio partita.
--------------------------------------------------------------------------*/
void ValutaMossa
( int *Tavola,     // scacchiera di gioco
  int Nvuote,      // numero delle caselle vuote
  int Who,         // giocatore che deve muovere: 1 o 2
  int *DoveMeglio, // casella della mossa migliore
  int *MaxVale     // valore della scacchiera vista da Who:
                   // <=-1 sconfitta, 0 pareggio, >=1 vittoria.
)
{ int i,           // indice sulle caselle della scacchiera
  Dove,            // miglior contromossa dell'avversario
  Vale;            // valore della contromossa

  // valuta tutte le mosse possibili;
  for (*MaxVale= -99, i=9; i ; i--)
    if (Tavola[i]==0) // se la casella Š vuota
    { Tavola[i] = Who; // occupa la casella
      if ( Tavola[1] & Tavola[2] & Tavola[3] ||     // 1° rigo
           Tavola[4] & Tavola[5] & Tavola[6] ||     // 2° rigo
           Tavola[7] & Tavola[8] & Tavola[9] ||     // 3° rigo
           Tavola[1] & Tavola[4] & Tavola[7] ||     // 1° colonna
           Tavola[2] & Tavola[5] & Tavola[8] ||     // 2° colonna
           Tavola[3] & Tavola[6] & Tavola[9] ||     // 3° colonna
           Tavola[1] & Tavola[5] & Tavola[9] ||     // diagonale /
           Tavola[7] & Tavola[5] & Tavola[3] )      // diagonale \
      { Vale = Nvuote;          // mossa vincente
      }
      else if (Nvuote == 1)     // se era l'unica casella vuota, pareggio
      { Vale = 0;
      }
      else // situazione ancora tutta da valutare
      {    // chiede il parere dell'avversario, e poi considera il suo
           // miglior punteggio (Vale) cambiato di segno (strategia NegMax)
        ValutaMossa (Tavola, Nvuote-1, Who^3, &Dove, &Vale);
        Vale = -Vale;
      }
      if (Vale > *MaxVale) *MaxVale = Vale, *DoveMeglio = i;
      Tavola[i] = 0; // libera la casella
    }
}

/*===========================================================================
Maschera di gioco
--------------------------------------------------------------------------*/
void Maschera
( int Gioc     // -1 maschera per la configurazione iniziale
               //  0 maschera gioco Umano - Umano
               //  1 maschera gioco Computer - Umano
               //  2 maschera gioco Umano - Computer
)
{ int i;

  clrscr();
  textbackground(4); textcolor(0);
  gotoxy( 2, 1); for (i=2; i<=79; cprintf(" "),i++);
  gotoxy(37, 1); cprintf("T R I S");
  gotoxy( 2,25); for (i=2; i<=79; cprintf(" "),i++);
  gotoxy(58,25); cprintf("di Luigi Lamberti");

  textbackground(0); textcolor(2);
  gotoxy (44,  6); cprintf ("+---------+---------+---------+");
  gotoxy (44,  7); cprintf ("|         |         |         |");
  gotoxy (44,  8); cprintf ("|         |         |         |");
  gotoxy (44,  9); cprintf ("|7        |8        |9        |");
  gotoxy (44, 10); cprintf ("+---------+---------+---------+");
  gotoxy (44, 11); cprintf ("|         |         |         |");
  gotoxy (44, 12); cprintf ("|         |         |         |");
  gotoxy (44, 13); cprintf ("|4        |5        |6        |");
  gotoxy (44, 14); cprintf ("+---------+---------+---------+");
  gotoxy (44, 15); cprintf ("|         |         |         |");
  gotoxy (44, 16); cprintf ("|         |         |         |");
  gotoxy (44, 17); cprintf ("|1        |2        |3        |");
  gotoxy (44, 18); cprintf ("+---------+---------+---------+");

  if (Gioc == -1)
  { textcolor(11);
    gotoxy (2, 3); cprintf ("F1 : Umano - Umano");
    gotoxy (2, 5); cprintf ("F2 : Computer - Umano");
    gotoxy (2, 7); cprintf ("F3 : Umano - Computer");
    gotoxy (2,13); cprintf ("ESC : interrompe gioco");
    textcolor(1);
    gotoxy (2, 9); cprintf ("F5 : help mossa migliore");
    gotoxy (2,11); cprintf ("123..789 : inserisci mossa");
  }
  else
  { textcolor(1);
    gotoxy (2, 3); cprintf ("F1 : Umano - Umano");
    gotoxy (2, 5); cprintf ("F2 : Computer - Umano");
    gotoxy (2, 7); cprintf ("F3 : Umano - Computer");
    textcolor(11);
    gotoxy (2, 9); cprintf ("F5 : help mossa migliore");
    gotoxy (2,11); cprintf ("123..789 : inserisci mossa");
    gotoxy (2,13); cprintf ("ESC : interrompe gioco");
    switch (Gioc)
    { case 0: gotoxy (13, 3); cprintf ("Umano - Umano"); break;
      case 1: gotoxy (13, 5); cprintf ("Computer - Umano"); break;
      case 2: gotoxy (13, 7); cprintf ("Umano - Computer");
    }
  }
}

/*============================================================= InKey ======
  Lettura di un carattere da tastiera: attende che venga battuto;
  OUT: Il codice Ascii oppure, il codice del tasto speciale:
  Return:13 Esc:27 F1:315 F2:316 F3:317 F5:319 ecc...
-------------------------------------------------------------------------*/
int InKey (void)
{ int Tast; // codice numerico del tasto premuto;

  Tast = getch();
  if (Tast == 0) Tast = getch() + 256;

  return ( Tast );
}