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

int  CercaCertezze (int Tav[][9][10], int *Nvuote );
int  CercaSudoku   (int Tav[][9][10], int Nvuote );
int  Congruenza    (int Tav[][9][10] );
void CopiaTavola   (int Source[][9][10], int Destin[][9][10]);
void InitTavola    (int Tav[][9][10], int *Nvuote );
void LeggiTavola   (int Tav[][9][10], int *Nvuote );
void ScriviCella   (int Tav[][9][10], int r, int c, int n, int *Nvuote);
void StampaTavola  (int Tav[][9][10], int Nvuote );

/*=============================================================================
             S U - D O K U
            ^^^^^^^^^^^^^^^ 
Un Sudoku è una griglia quadrata di 81 celle: 9 righe orizzontali per 9 
colonne verticali; inoltre, la griglia è divisa in 9 riquadri 3x3 (gruppi, 
col bordo più spesso) di 9 celle ciascuno. 

Ciascuna riga orizzontale, colonna verticale e riquadro di 3x3 celle 
contiene una sola volta tutti i numeri da 1 a 9; pertanto in nessuna riga, 
colonna e riquadro può esserci un numero ripetuto. 

TAVOLA DI GIOCO
^^^^^^^^^^^^^^^     0   1   2   3   4   5   6   7   8 
                  *===*===*===*===*===*===*===*===*===*
                0 H   |   |   H   |   |   H   |   |   H
                  *---+---+---*---+---+---*---+---+---*
                1 H   |   |   H   |   |   H   |   |   H
                  *---+---+---*---+---+---*---+---+---*
                2 H   |   |   H   |   |   H   |   |   H
                  *===*===*===*===*===*===*===*===*===*
                3 H   |   |   H   |   |   H   |   |   H
                  *---+---+---*---+---+---*---+---+---* 
                4 H   |   |   H   |   |   H   |   |   H
                  *---+---+---*---+---+---*---+---+---* 
                5 H   |   |   H   |   |   H   |   |   H
                  *===*===*===*===*===*===*===*===*===*
                6 H   |   |   H   |   |   H   |   |   H
                  *---+---+---*---+---+---*---+---+---* 
                7 H   |   |   H   |   |   H   |   |   H
                  *---+---+---*---+---+---*---+---+---* 
                8 H   |   |   H   |   |   H   |   |   H
                  *===*===*===*===*===*===*===*===*===*

CELLA
^^^^^ Ogni Cella della Tavola di gioco è un array di 10 caselle
      *---+---+---+---+---+---+---+---+---+---+
      |   |   |   |   |   |   |   |   |   |   | 
      *---+---+---+---+---+---+---+---+---+---+
        0   1   2   3   4   5   6   7   8   9
La casella 0 indica se la cella è libera (0) o occupata (1..9).
Le nove caselle successive indicano ciascuna la possibilità di inserire 
nella cella il valore corrispondente (Flag 1=inserimento possibile).

GRUPPI
^^^^^^
Chiameremo Gruppi i 9 Riquadri 3x3 interni. 
Detta (i,j) una cella della Tavola, le coordinate della cella in alto a 
sinistra del suo gruppo di appartenenza saranno:
ig = (i / 3) * 3 = i - i % 3
jg = (j / 3) * 3 = j - j % 3
Il gruppo sarà composto dalle nove celle (y,x) con 
ig <= y <= ig+2 e jg <= x <= jg+2 
----------------------------------------------------------------- 9.10.2005 -*/ 

void main (void)
{
  int Tav [9][9][10], // 1° riga, 2° colonna, 3° campi della cella
      Nvuote;         // Numero di caselle vuote nella tavola 

  InitTavola (Tav, &Nvuote);
  LeggiTavola (Tav, &Nvuote);
  StampaTavola (Tav, Nvuote);
  printf("\nbatti un tasto per iniziare l'analisi ...\n"); _getch();

  // Dapprima cerca di risolvere il gioco con posizioni certe
  while ( CercaCertezze (Tav, &Nvuote) ); 
  StampaTavola (Tav, Nvuote);

  // In seconda battuta esegue una ricerca combinatoria ricorsiva
 
if ( ! Nvuote )
    printf ("\nSoluzione trovata.\n");
  else 
  { printf("\nbatti un tasto per la ricerca combinatoria ...\n"); 
    _getch();
    if ( CercaSudoku(Tav,Nvuote) ) 
    { StampaTavola (Tav, 0);
      printf ("\nSoluzione trovata.\n");
    } 
    else
      printf("\nNON esistono soluzioni\n"); 
  }

  _getch();
}

/*=============================================================================
 Cerca di completare la tavola da gioco, ALMENO con una cella vuota, provando 
 ad inserire un valore in una cella vuota, ove il valore sia possibile.
 Se non sorgono incongruenze 
   se esitono altre caselle vuote, chiama ricorsivamente se stessa 
   se sono finite le caselle vuote, trovata una soluzione.
 Se non è stata trovata una soluzione, prova con un altro valore nella stessa 
 cella. Al termine dei valori possibili, senza successo, restituisce 0: 
 ( è inutile ricominciare con un'altra cella ! )

 OUTPUT: 1 soluzione trovata e copiata sulla Tavola da gioco
         0 nessuna soluzione
-----------------------------------------------------------------------------*/

int CercaSudoku
( int Tav[][9][10], 
  int Nvuote           // Numero di caselle vuote nella tavola 
)
{ int r, c,            // riga e colonna della cella da occupare 
     
N,               // valore da inserire 
     
Tav2 [9][9][10], // Tavola di appoggio 
      Nv2,             // numero delle caselle vuote da riempire 
     
i, j,
      Trovato;         // 0 nessuna soluzione possibile; 
                       // 1 soluzione trovata e copiata su Tav 

  for (i=9; i; )       // cerca una cella vuota, che CERTAMENTE ESISTE
   
for (i--, j=9; j; ) 
      if ( ! Tav[i][--j][0] ) r=i, c=j;

  for (N=9, Trovato=0; N && !Trovato; N--)
  if ( Tav[r][c][N] )  // se è possibile inserire il valore N 
 

    CopiaTavola (Tav, Tav2);
    Nv2 = Nvuote;
    ScriviCella(Tav2, r, c, N, &Nv2); 
    while ( CercaCertezze(Tav2, &Nv2) );

    if ( Congruenza(Tav2) )
    { Trovato = Nv2 ? CercaSudoku (Tav2, Nv2) : 1;
      if (Trovato) CopiaTavola (Tav2, Tav); //copia la soluzione
    } 
  }
  return Trovato;
}

/*=============================================================================
 Cerca i valori CERTI da inserire sulla tavola di gioco, esaminando le Flag 
 "possibilità" delle celle: 
 -) se una cella vuota possiede una sola flag==1, inseriamo quel valore;
 -) per ogni riga, colonna e gruppo, se un valore è disponibile in una sola 
 posizione vuota, inseriamo il valore.

 OUTPUT: quantità dei valori inseriti
------------------------------------------------------------------------------*/
int CercaCertezze
( int Tav[][9][10],
  int *Nvuote 
)
{ int i, j, k, 
      r, c,       // riga e colonna della cella da occupare 
      rg, cg,     // angolo in alto a sinistra del gruppo di appartenenza 
      N,          // valore da inserire 
      Flag,       // numero di Flag alte per ciascuna cella. 
      Tot;        // Totale dei numeri inseriti nella sessione 

  Tot = 0;
  for (i=0; i<9; i++) // ricerca ed analisi delle celle vuote 
    for (j=0; j<9; j++) 
      if (! Tav [i][j][0]) // se Cella vuota
      { for (Flag=0, k=9; k; k--) 
          if (Tav[i][j][k]) Flag++, N=k; // conta Flag possibilità
        if (Flag == 1) 
          ScriviCella(Tav, i, j, N, Nvuote), Tot ++; // se unico possibile
      }
  for (N=9; N; N--) // Ricerca per ogni valore da inserire
  { 
    for (i=0; i<9; i++) // esamina ciascuna riga della tavola 
    { for (Flag= j= 0; j<9; j++) 
        if (Tav[i][j][N]) Flag++, c=j; // conta Flag possibilità
      if (Flag==1 && !Tav[i][c][0])    // se unico possibile e cella vuota
        ScriviCella(Tav, i, c, N, Nvuote), Tot ++; 
    }
    for (j=0; j<9; j++) // esamina ciascuna colonna della tavola 
    { for (Flag= i= 0; i<9; i++) 
        if (Tav[i][j][N]) Flag++, r=i; // conta Flag possibilità
      if (Flag==1 && !Tav[r][j][0])    // se unico possibile e cella vuota
        ScriviCella(Tav, r, j, N, Nvuote), Tot ++; 
    }
    for (rg=0; rg<9; rg+=3) // esamina ciascun riquadro della tavola 
      for (cg=0; cg<9; cg+=3) 
      { for (Flag= i= 0; i<3; i++) 
          for (j=0; j<3; j++) 
            if (Tav[rg+i][cg+j][N]) Flag++, r=rg+i, c=cg+j; // conta Flag
        if (Flag==1 && !Tav[r][c][0])  // se unico possibile e cella vuota
          ScriviCella(Tav, r, c, N, Nvuote), Tot ++; 
      }
  }
  return Tot;
}

/*=============================================================================
 Valuta la congruenza di uno schema di gioco: per ogni riga, ogni colonna e
 ogni riquadro ciascun valore da 1 a 9 deve essere o già presente o possibile
 in almeno una cella. 
 Poiché i valori inseriti conservano la flag di possibilità, è sufficiente
 esaminare le caselle delle flag possibilità.

 OUTPUT: 0 la tavola è inconguente e NON è possibile trovare una soluzione
         1 il gioco può proseguire alla ricerca di soluzioni.
-----------------------------------------------------------------------------*/
int Congruenza
( int Tav[][9][10]
)
{ int i, j,
      N,         // valore da inserire 
      Flag,      // numero di flag == 1 
      rg, cg,    // angolo in alto a sinistra del gruppo di appartenenza 
      Congrua;   // 0 = tavola inconguente

  Congrua = 1;
                               // ogni valore da inserire deve essere o già
  for (N=9; N && Congrua; N--) // presente o possibile in almeno una cella
  {
    for (i=0; i<9; i++)        // esamina ciascuna riga della tavola 
    { for (Flag= j= 0; j<9; Flag += Tav[i][j++][N]); // conta le flag alte
      if (! Flag) Congrua = 0;
    }
    for (j=0; j<9 && Congrua; j++) // esamina ciascuna colonna della tavola 
    { for (Flag= i= 0; i<9; Flag += Tav[i++][j][N]); // conta le flag alte
      if (! Flag) Congrua = 0;
    }
    for (rg=0; rg<9 && Congrua; rg+=3) // esamina ciascun riquadro 
      for (cg=0; cg<9 && Congrua; cg+=3)
      { for (Flag= i= 0; i<3; i++)
          for (j=0; j<3; Flag += Tav[rg+i][cg+j][N], j++);
        if (! Flag) Congrua = 0;
      }
  }
  return Congrua;
}

/*=============================================================================
 Duplica una tavola da gioco.
-----------------------------------------------------------------------------*/
void CopiaTavola
( int Source[][9][10], 
  int Destin[][9][10]  
)
{ int i, j, k; 

  for (i=9; i; )
    for (i--, j=9; j; ) 
      for (j--, k=10; k; k--, Destin[i][j][k] = Source[i][j][k]);
}

/*=============================================================================
 Inizializza la Tavola di gioco del Sudoku, svuotando le celle e 
 rendendo potenzialmente inseribili tutti i valori da 1 a 9.
-----------------------------------------------------------------------------*/
void InitTavola
( int Tav[][9][10],
  int *Nvuote         // Numero di caselle vuote nella tavola 
)
{ int i, j, k;
 
  for (*Nvuote = 81, i=0; i<9; i++) 
    for (j=0; j<9; j++) 
    { Tav [i][j][0] = 0;
      for (k=9; k; Tav [i][j][k--] = 1 );
    } 
}

/*=============================================================================
 Carica un gioco da risolvere (sodoku parziale) dal file Sudoku.txt
 Saranno esaminati solo i primi 9 records del file e, di questi, solo i 
 primi 9 caratteri: il resto del file può essere occupato da commenti.
 Tutti i caratteri diversi da 1,2,3,4,5,6,7,8 e 9, sono considerati Spazi.

 ...7..4..
 .3..9..2.
 4....5...
 ..8.....5
 .9..3..7.
 6.....3..
 ...4....1
 .7..2..9.
 ..5..8...

-----------------------------------------------------------------------------*/
void LeggiTavola
( int  Tav[][9][10], 
  int  *Nvuote
)
{ int  i, j;
  FILE *Pf;
  char Buf[200];

  Pf = fopen ("Sudoku.txt", "r");
  if (Pf)
  { for (*Nvuote=81, i=0; i<9; i++)
    { fscanf(Pf,"%s",Buf);
      for (j=0; j<9; j++) 
        if (Buf[j]>=49 && Buf[j]<=57) 
          ScriviCella (Tav, i, j, Buf[j]-48, Nvuote);
    } 
    fclose (Pf);
  } 
}

/*=============================================================================
 Inserisce un valore da 1 a 9 in una cella vuota.
 Tutte le celle della stessa riga, stessa colonna e stesso gruppo (riquardo)
 perdono la possibilità di ricevere quel valore.
-----------------------------------------------------------------------------*/
void ScriviCella
( int Tav[][9][10],  // Tavola di gioco
  int r,             // riga dell'inserimento
  int c,             // colonna dell'inserimento
  int n,             // numero da inserire: 1 <= n <= 9
  int *Nvuote        // Numero di caselle vuote nella tavola 
)
{ int i, j, k,
      rg, cg;        // angolo in alto a sinistra del gruppo di appartenenza 

  (*Nvuote) --;
  Tav [r][c][0] = n;                 // inserisce il numero
  for (k=9; k; Tav [r][c][k--] = 0); // ovviamente, esclude tutti gli altri

  for (j=9; j; Tav [r][--j][n] = 0); // elimina flag dalle celle della riga
  for (i=9; i; Tav [--i][c][n] = 0); // elimina flag dalle celle della colonna

  rg = (r / 3) * 3;                  // angolo in alto a sinistra del gruppo 
  cg = (c / 3) * 3;
  for (i=0; i<3; i++)                // elimina flag dalle celle del gruppo
    for (j=0; j<3; j++) 
      Tav [rg+i][cg+j][n] = 0;

  Tav [r][c][n] = 1;                 // conferma la flag sulla stessa cella 
}

/*=============================================================================
 Visualizza la matrice del campo di gioco.
-----------------------------------------------------------------------------*/
void StampaTavola
( int Tav[][9][10], 
  int Nvuote
)
{ int i, j;

  for (i=0; i<9; i++) 
  { if (i%3) printf ("\n +---+---+---H---+---+---H---+---+---+\n |");
    else printf ("\n *===========H===========H===========*\n |");
    for (j=0; j<9; j++) 
    { if (Tav [i][j][0]) printf (" %d ",Tav [i][j][0]);
      else               printf (" "); 
      if (j==2 || j==5)  printf ("H"); 
      else               printf ("|"); 
    } 
  }
  printf ("\n *===========H===========H===========* Vuote = %d\n", Nvuote);
}