sudoku.php

  1. <?php
  2. /*************************/
  3. /*  Resolveur de Sudoku  */
  4. /*      By Clems, 2006   */
  5. /*************************/
  6. $start = explode(' ', microtime());
  7.  
  8. /*function vide (int)
  9. Sert pour savoir si le sudoku est rempli ou non
  10. Fonction de callback par array_filter*/
  11. function vide($valeur)
  12. {
  13.    return ($valeur == 0); //renvoie TRUE si $valeur vaut 0, FALSE sinon
  14. }
  15.  
  16. /*function affichage(array)
  17. Sortie Html pour affiche le sudoku de maniere comprehensible*/
  18. function affichage($sudoku)
  19. {
  20.    echo '<table style="border: 1px solid black; border-colapse: colaspe">';//initialisation du tableau
  21.    $sudoku = decoupe($sudoku);//on rend le sudoku plus organise, pour avoir les coordonnees  x|y  des cases
  22.    for($i=1; $i<10; $i++)
  23.    {
  24.       echo '<tr>'; //ligne
  25.       for($j=1; $j<10; $j++)
  26.       {
  27.          echo '<td style="'; //cellule
  28.          if($j%3 == 0 && $j != 9) echo 'border-right: 1px solid black;'; //si c'est la droite d'un carre, on trace la bordure
  29.          if($i%3 == 0 && $i != 9) echo 'border-bottom: 1px solid black;'; //si c'est le bas d'un carre, on trace la bordure
  30.          echo '"><input size="1" readonly="readonly" maxlength="1" value="';
  31.          if ($sudoku['ligne'.$i][$j] != 0) echo $sudoku['ligne'.$i][$j]; //si la case n'est pas vide, on l'affiche
  32.          echo '"/></td>';
  33.       }
  34.       echo '</tr>';//fin ligne
  35.    }
  36.    echo '</table><br />'; //fin de l'affichage
  37. }
  38.  
  39. /*function decoupe(array)
  40. Decoupe le sudoku de maniere a connaitre le contenu de chaques lignes, colones, et carre, pour connaitre plus tard les valeurs admissibles pour les cases vides*/
  41. function decoupe($sudoku)
  42. {   
  43.    $decoupe = array();//initialisation de l'array
  44.    for($x=1; $x<10; $x++) //pour chaque ligne
  45.    {
  46.       ${'ligne'.$x} = array(); //initialisation de la ligne
  47.       for($y=1; $y<10; $y++) //chaque cellule de la ligne en cours
  48.       {
  49.          ${'ligne'.$x}[$y] = $sudoku[$x.'|'.$y]; //on remplis l'array "ligne_numero X" avec la valeur qui va bien
  50.       }
  51.       $decoupe['ligne'.$x] = ${'ligne'.$x};//et on mets la ligne dans l'array qui contient tout => $decoupe
  52.    }
  53.    for($y=1; $y<10; $y++) // Bon, la, je refais pas tout les commentaires, c'est exactement pareil, mais avec les colones :-D
  54.    {
  55.       ${'colone'.$y} = array();
  56.       for($x=1; $x<10; $x++)
  57.       {
  58.          ${'colone'.$y}[$x] = $sudoku[$x.'|'.$y];
  59.       }
  60.       $decoupe['colone'.$y] = ${'colone'.$y};
  61.    }
  62.    $i = 1; //initialisation du compteur pour le numero des carres
  63.    for($centre_x=2; $centre_x<=8; $centre_x+=3)//pour chaque centre de carre
  64.    {
  65.       for($centre_y=2; $centre_y<=8; $centre_y+=3)//idem : pour chaque centre de carre
  66.       {
  67.          ${'carre'.$i} = array();//initialisation du carre_munero_I
  68.          $j = 1; //initialisation du compteur pour les cases des carres
  69.          for($x=$centre_x-1; $x<=$centre_x+1; $x++)// la...
  70.          {
  71.             for($y=$centre_y-1; $y<=$centre_y+1; $y++)//... et la, on fait le tour du centre du carre, donc on parcour tout le carre :-)
  72.             {
  73.                ${'carre'.$i}[$j] = $sudoku[$x.'|'.$y]; //et on mets la valeur de la case correspondante dans l'array  carre_munero_I...
  74.                $j++; //case suivante
  75.             }
  76.          }
  77.          $decoupe['carre'.$i] = ${'carre'.$i}; //... qu'on met ensuite dans l'array qui contient tout => $decoupe
  78.          $i++; //carre suivant
  79.       }
  80.    }
  81.    return $decoupe; //et on revoie le tout,  bien ficele :-)
  82. }
  83.  
  84. /*function possible(string CASE, array SUDOKU)
  85. Renvoie les valeurs possibles pour la case CASE, en se basant sur le sudoku SUDOKU*/
  86. function possible($case, $sudoku)
  87. {   
  88.    $coord_case = explode('|', $case); //decoupe des coordonnees
  89.    $x_case = $coord_case[0]; //l'abscisse de la case
  90.    $y_case = $coord_case[1]; //l'ordonnee de la case
  91.    
  92.    $decoupe = decoupe($sudoku); //on decoupe le sudoku
  93.    $possible = array(1, 2, 3, 4, 5, 6, 7, 8, 9); //toute les valeurs possible pour une case
  94.    $possible = array_diff($possible, $decoupe['ligne'.$x_case]); //on enleve a ces valeurs toutes celles de la ligne qui contient la case
  95.    $possible = array_diff($possible, $decoupe['colone'.$y_case]); //on enleve a ces valeurs toutes celles de la colone qui contient la case
  96.    
  97.    if($y_case <= 3 && $x_case <=3) $possible = array_diff($possible, $decoupe['carre1']); //ca...
  98.    elseif($y_case <= 6 && $x_case <= 3) $possible = array_diff($possible, $decoupe['carre2']); //... et ca...
  99.    elseif($y_case <= 9 && $x_case <= 3) $possible = array_diff($possible, $decoupe['carre3']); //... et ca...
  100.    elseif($y_case <= 3 && $x_case <= 6) $possible = array_diff($possible, $decoupe['carre4']); //... et ca...
  101.    elseif($y_case <= 6 && $x_case <= 6) $possible = array_diff($possible, $decoupe['carre5']); //... et ca...
  102.    elseif($y_case <= 9 && $x_case <= 6) $possible = array_diff($possible, $decoupe['carre6']); //... et ca...
  103.    elseif($y_case <= 3 && $x_case <= 9) $possible = array_diff($possible, $decoupe['carre7']); //... et ca...
  104.    elseif($y_case <= 6 && $x_case <= 9) $possible = array_diff($possible, $decoupe['carre8']); //... et ca...
  105.    elseif($y_case <= 9 && $x_case <= 9) $possible = array_diff($possible, $decoupe['carre9']); //... et ca, ca sert a determiner dans quel carre se trouve la case, et a enlever au valeurs possibles toutes celles deja mise dans le carre
  106.    
  107.    if(!empty($possible)) //si il y a une ou des valeurs possibles
  108.    {
  109.       return array_values($possible); //on les renvoie
  110.    }
  111.    else
  112.    {
  113.       return FALSE; //sinon, FALSE, pour dire que la case est impossible a remplir :'(
  114.    }
  115. }
  116.  
  117. /*function init()
  118. Initialise le sudoku en fonction des valeurs envoyees via le formulaire*/
  119. function init()
  120. {
  121.    $a_remplir = array(); //on initialise le tableaux qui va contenir toutes les cases devant etre remplies (celles laissees vides par l'utilisateur, quoi :-p)
  122.    $sudoku = array(); //et on initialise l'array sudoku, qui contiendra les valeurs de chaques cases.
  123.    for($x=1; $x<10; $x++) //ca...
  124.    {
  125.       for($y=1; $y<10; $y++)// ... et ca, c'est pour parcourir toutes les cases
  126.       {
  127.          $sudoku[$x.'|'.$y] = (intval($_POST[$x.'|'.$y]) < 10 && intval($_POST[$x.'|'.$y] > 0)) ? intval($_POST[$x.'|'.$y]) : 0; //si la valeur envoyer par l'utilisateur et bien compris entre 1 et 9, on la mets dans l'array SUDOKU, sinon, on mets 0 a la place, comme case a remplir
  128.          if ($sudoku[$x.'|'.$y] == 0) $a_remplir[] = array($x.'|'.$y, '', 0); //et si la case du soduku est vide, on l'ajoute a l'array A_REMPLIR,  qui va contenir toutes les cases devant etre remplies
  129.       }
  130.    }
  131.    return array($sudoku, $a_remplir); //et on renvoie l'ensemble
  132. }
  133.  
  134. function verif_grille($sudoku)
  135. {
  136.    $decoupe = decoupe($sudoku);
  137.    $erreur = '';
  138.    for($i = 1; $i<10; $i++)
  139.    {
  140.       if(array_diff($decoupe['ligne'.$i], array_filter($decoupe['ligne'.$i], "vide")) != array_unique(array_diff($decoupe['ligne'.$i], array_filter($decoupe['ligne'.$i], "vide"))))
  141.       {
  142.          $erreur .= 'La ligne '.$i.' comporte une erreur : 2 chiffres ou plus sont identiques.<br />';
  143.       }
  144.       if(array_diff($decoupe['colone'.$i], array_filter($decoupe['colone'.$i], "vide")) != array_unique(array_diff($decoupe['colone'.$i], array_filter($decoupe['colone'.$i], "vide"))))
  145.       {
  146.          $erreur .= 'La colone '.$i.' comporte une erreur : 2 chiffres ou plus sont identiques.<br />';
  147.       }
  148.       if(array_diff($decoupe['carre'.$i], array_filter($decoupe['carre'.$i], "vide")) != array_unique(array_diff($decoupe['carre'.$i], array_filter($decoupe['carre'.$i], "vide"))))
  149.       {
  150.          $erreur .= 'Le carre '.$i.' comporte une erreur : 2 chiffres ou plus sont identiques.<br />';
  151.       }
  152.    }
  153.    return $erreur;
  154. }
  155.  
  156. /*function retour(int NUMERO_CASE_ORIGIN)
  157. Si la case a remplir numero NUMERO_CASE_ORIGIN  + 1 est impossible a remplir, on revient en arriere, en modifiant la case precedente numero NUMERO_CASE_ORIGIN, de maniere a avoir des valeurs possibles*/
  158. function retour($numero_case)
  159. {
  160.    global $a_remplir, $sudoku, $nb_case_remplie, $erreur_commise; //on recupere toutes les variables dont on a besoin
  161.    $erreur_commise += 1;
  162.    $case_a_modif = $a_remplir[$numero_case][0]; //on determine les coordonees de la case a remplir, grace a son numero, et a l'array A_REMPLIR
  163.    $valeur_possible = $a_remplir[$numero_case][1]; //on determine les valeurs possibles de la case a remplir, grace a son numero, et a l'array A_REMPLIR
  164.    $valeur_test = $a_remplir[$numero_case][2]; //on determine quelles valeurs possibles de la case a remplir ont deja ete testees, grace a son numero, et a l'array A_REMPLIR
  165.    if (!empty($valeur_possible[$valeur_test])) //Si il existe encore une valeur possible non-teste
  166.    {
  167.       $sudoku[$case_a_modif] = $valeur_possible[$valeur_test]; //on remplace l'ancienne valeur par celle-ci
  168.       $a_remplir[$numero_case][2] += 1; //et on signal que l'on vient de tester une valeur de plus
  169.       $possible = possible($a_remplir[$numero_case+1][0], $sudoku); //on redefinit les nouvelles valeurs possible pour la case suivante (celle qui bloquait
  170.       $a_remplir[$numero_case+1][1] = $possible; //et on les insere dans l'array A_REMPLIR qui les retients
  171.    }
  172.    else //sinon, plus de valeur possible, cette case n'est donc pas modifiable, il faut revenir a celle d'avant encore
  173.    {
  174.       $sudoku[$case_a_modif] = 0; //on remet la case a 0 ( = a remplir)
  175.       $a_remplir[$numero_case][2] = 0; //on remet son compteur de case teste a 0
  176.       $nb_case_remplie--; //et on signal que l'on est revenu une case en arriere
  177.       $numero_case = ($numero_case > 1) ? $numero_case-1 : 0; //on verifie qu'on donne pas un numero de case negatif(il n'y a pas de numero de case negatif :-p), sinon, on le remet a 0
  178.       retour($numero_case);//ET REBELOTTE, on recommence :-D
  179.    }
  180. }
  181.  
  182. //une grille par defaut
  183. $grille_defaut = array('1|1' => '',
  184.                   '1|2' => '',
  185.                   '1|3' => 5,
  186.                   '1|4' => '',
  187.                   '1|5' => '',
  188.                   '1|6' => 7,
  189.                   '1|7' => '',
  190.                   '1|8' => '',
  191.                   '1|9' => 1,
  192.                   '2|1' => '',
  193.                   '2|2' => '',
  194.                   '2|3' => '',
  195.                   '2|4' => '',
  196.                   '2|5' => '',
  197.                   '2|6' => '',
  198.                   '2|7' => 4,
  199.                   '2|8' => 6,
  200.                   '2|9' => 3,
  201.                   '3|1' => 8,
  202.                   '3|2' => 1,
  203.                   '3|3' => 9,
  204.                   '3|4' => '',
  205.                   '3|5' => '',
  206.                   '3|6' => '',
  207.                   '3|7' => '',
  208.                   '3|8' => '',
  209.                   '3|9' => '',
  210.                   '4|1' => '',
  211.                   '4|2' => '',
  212.                   '4|3' => '',
  213.                   '4|4' => 3,
  214.                   '4|5' => 1,
  215.                   '4|6' => 5,
  216.                   '4|7' => '',
  217.                   '4|8' => '',
  218.                   '4|9' => '',
  219.                   '5|1' => 2,
  220.                   '5|2' => '',
  221.                   '5|3' => 3,
  222.                   '5|4' => '',
  223.                   '5|5' => '',
  224.                   '5|6' => '',
  225.                   '5|7' => 8,
  226.                   '5|8' => '',
  227.                   '5|9' => 7,
  228.                   '6|1' => '',
  229.                   '6|2' => '',
  230.                   '6|3' => '',
  231.                   '6|4' => 7,
  232.                   '6|5' => 2,
  233.                   '6|6' => 8,
  234.                   '6|7' => '',
  235.                   '6|8' => '',
  236.                   '6|9' => '',
  237.                   '7|1' => '',
  238.                   '7|2' => '',
  239.                   '7|3' => '',
  240.                   '7|4' => '',
  241.                   '7|5' => '',
  242.                   '7|6' => '',
  243.                   '7|7' => 5,
  244.                   '7|8' => 9,
  245.                   '7|9' => 6,
  246.                   '8|1' => 6,
  247.                   '8|2' => 2,
  248.                   '8|3' => 8,
  249.                   '8|4' => '',
  250.                   '8|5' => '',
  251.                   '8|6' => '',
  252.                   '8|7' => '',
  253.                   '8|8' => '',
  254.                   '8|9' => '',
  255.                   '9|1' => 1,
  256.                   '9|2' => '',
  257.                   '9|3' => '',
  258.                   '9|4' => 4,
  259.                   '9|5' => '',
  260.                   '9|6' => '',
  261.                   '9|7' => 3,
  262.                   '9|8' => '',
  263.                   '9|9' => '');
  264.                   
  265. if(!empty($_POST)) //si le formulaire est soumis
  266. {
  267.    $init = init(); //on initialise le tout
  268.    $sudoku = $init[0]; //on definit l'array SUDOKU
  269.    $a_remplir = $init[1]; //on definit l'array A_REMPLIR
  270.    $valide = verif_grille($sudoku);
  271.    if($valide == '')
  272.    {   
  273.       $sudoku_depart = $sudoku; //ca, c'est juste pour garder en memoire la grille de depart, et l'afficher a la fin
  274.       
  275.       $is_vide = array_filter($sudoku, "vide"); //on verifie que l'utilisateur a pas soumis une grille deja pleine :-p (on sait jamais :-p)
  276.       $nb_case_remplie = 0; //et on signale qu'on n'a pas encore remplis de case
  277.       
  278.       $erreur_commise = 0;
  279.       $coup = 0;
  280.       $start = explode(' ', microtime()); //temps de depart
  281.  
  282.       while(!empty($is_vide)) //tant que le sudoku n'est pas plein, on execute la routine de remplissage suivant :
  283.       {
  284.          $coup++;
  285.          $case_a_remplir = $a_remplir[$nb_case_remplie][0]; //definition des coordonnees de la case a remplir grace a son numero, et l'array A_REMPLIR
  286.          $possible = possible($case_a_remplir, $sudoku); //on determine les valeurs possibles de la case a remplir, grace a son numero, et a l'array A_REMPLIR
  287.          if(!empty ($possible)) //si il y a au moins une valeur possible
  288.          {
  289.             $a_remplir[$nb_case_remplie][1] = $possible; //on remplis les caracteristique de la case, contenues dans l'array A_REMPLIR
  290.             $sudoku[$case_a_remplir] = $possible[0]; //et on mets la premiere valeur possible dans la case que l'on est en train de remplir
  291.             $a_remplir[$nb_case_remplie][2] += 1; //on signal que l'on vient de tester une valeur de plus
  292.             next($a_remplir); //et on passe a la case suivante, toujours contenu dans l'array  A_REMPLIR
  293.             $case_a_remplir = key($a_remplir);   //et on definit la nouvelle case a remplir
  294.             $nb_case_remplie++; //sans oublie de signaler que l'on vient de remplir une case de plus ;-)
  295.          }
  296.          else //sinon, si aucune valeur n'est possible,
  297.          {
  298.             retour($nb_case_remplie-1); //on appelle la function RETOUR
  299.          }
  300.          //affichage($sudoku);
  301.          $is_vide = array_filter($sudoku, "vide"); //on verifie que le sudoku n'est pas vide
  302.       }
  303.       //fin de la routine de remplissage
  304.       $stop = explode(' ', microtime());
  305.       $tps[0] = $stop[0]+$stop[1];
  306.       $tps[1] = $start[0]+$start[1];
  307.       $rendertime=number_format((($tps[0])-($tps[1])), '4');
  308.       
  309.       echo 'Sudoku de depart :'; //on passe a l'affichage :-D
  310.       affichage($sudoku_depart); //affiche de la grille de depart
  311.       $coup_total = $coup + $erreur_commise;
  312.       echo 'Solution trouvee en : '.$rendertime.' secondes ('.$coup_total.' coups, dont '.$erreur_commise.' erreurs) :';
  313.       affichage($sudoku); //et de la solution trouvee :-D
  314.       echo '<a href="sudoku.php">Retour</a>'; //lien de retour
  315.    }
  316.    else
  317.    {
  318.       echo '<strong>Grille invalide !</strong><br />'.$valide;
  319.    }
  320. }
  321. else //sinon, le formulaire
  322. {
  323.    echo 'Entrez une grille <strong>valide</strong> du sudoku ci-dessous : ';
  324.    if (!empty($_GET['def'])) echo'(<a href="sudoku.php">Grille vierge</a>)'; //si l'utilisateur a demander la grille par defaut
  325.    else echo'(<a href="sudoku.php?def=ok">Grille par defaut</a>)'; //sinon
  326.    echo '<br /><br/ ><!--by Clems--!><form action="sudoku.php" method="post">
  327.          <table style="border: 1px solid black; border-colapse: colaspe">';
  328.    
  329.    for($x=1; $x<10; $x++)
  330.    {
  331.       echo '<tr>';
  332.       for($y=1; $y<10; $y++)
  333.       {
  334.          echo '<td style="'; //cellule
  335.          if($x%3 == 0 && $x != 9) echo 'border-bottom: 1px solid black;'; //si c'est la droite d'un carre, on trace la bordure
  336.          if($y%3 == 0 && $y != 9) echo 'border-right: 1px solid black;'; //si c'est le bas d'un carre, on trace la bordure
  337.          $value = (!empty($_GET['def'])) ? $grille_defaut[$x.'|'.$y] : ''; //comment pre-remplir le formulaire
  338.          echo '"><input name="'.$x.'|'.$y.'" size="1" maxlength="1" value="'.$value.'"/></td>'; //affichage de la valeur de la grille par defaut
  339.       }
  340.       echo '</tr>';
  341.    }
  342.    echo '</table><br />
  343.       <input type="submit" value="go!" />
  344.       </form>';
  345. }
  346. ?>
Retour