Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

[PHP5][POO][JEUX] SUDOKU


Information sur la source

Catégorie :Jeux Classé sous : sudoku, php5, poo, heritage, algorithme Niveau : Expert Date de création : 19/04/2006 Date de mise à jour : 29/08/2006 13:24:22 Vu / téléchargé: 9 977 / 1 410

Note :
9,36 / 10 - par 11 personnes
9,36 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (36)
Ajouter un commentaire et/ou une note

Description

Cliquez pour voir la capture en taille normale
Bonjour,

Voici mon Sudoku que j'ai mis en ligne depuis maintenant un mois. (http://nifhell.free.fr)
Aussi vu qu'il ne présente pas de bug majeur, je me permets de le publier.
Des exemples d'utilisation sont dans le ZIP, ainsi que le code complet.


Quelques unes des fonctionnalités:
- Export en XML
- Import XML
- Export HTML
- Export texte brut
- Génération de grilles
- Résolution de grilles
- 7 niveaux de difficulté
- 2 types de grille: en chiffre ou en lettre (vous pouvez en rajouter: facilement customisable)
- Potentiellement peut créer des grilles de 25*25, 36*36, etc... cases => dépend des ressources système
Sur mon site FREE: est limité à des grilles de 9*9 cases
- Javascript de controle de saisie incorporé lors de l'export HTML
- Un mode tutoriel, affichant, lors de l'export HTML, des listes déroulantes contenant les possibilités aulieu de zones de saisie de texte vide
- Serialisable (pour se passser le sudoku dans les session) car Orienté Objet
Defaut:
- Les grilles ne sont pas forcément à solution unique
D'ailleur si quelqu'un à un tuyau, pour faire des grille à coup sur à solution unique, matheux de préférence, car j'aime pas les solutions toute faites...


Ci dessous une explication de l'algorithme de génération/résolution:

Définitions:
Un Sudoku est une grille dont la racine carré du nombre total de cases est un chiffre entier.
Soit T le nombre total de cases.
Soit N le nombre de symboles uniques (le plus souvent des chiffres) avec lesquel on remplira ces cases. N est égale à la racine carré du nombre total de case: N=sqrt(T).
Soit V l'ensemble des symboles uniques choisis (ex: 1 2 3 4 5 6 7 8 9)

La grille du Sudoku définit un repère, R1, dont l'origine, O1, est la case la plus en haut à gauche de la grille.
La grille du Sudoku est donc composée de N lignes, de N colonnes et de N boîtes, composées elle-même de N cases.

Chaque case se définit par son abscisse, I, et son ordonnée, J, définis par rapport à l'origine O1.

Une boîte est un ensemble de N cases disposées en carré, et se définit par une abscisse, X, et une ordonnée, Y, relative à O1.
Une boîte définie donc elle aussi un repère, R2, relatif à R1, dontl'origine, O2, est la case la plus en haut à gauche de cette boîte:
Si I, J sont respectivement l'abscisse et l'ordonnée dans le repère R1 de la case la plus en haut à gauche de la boîte, alors O2 à pour coordonnées (X, Y) relatives à O1: (X, Y) = (entier(I/N), entier(J/N))
Aussi chaque case possède des coordonnées (A,B) relatives au repère de leur boîte respective, R2, définies par: (A, B) = (I-N*X, J-N*Y)

Le but étant de remplir la grille avec les symboles choisit parmi les N symboles uniques.
Les cases se remplissent suivant les contraintes suivantes:
Toutes les cases d'une même ligne doivent comporter un symbole différent
Toutes les cases d'une même colonne doivent comporter un symbole différent
Toutes les cases d'une même boîte doivent comporter un symbole différent

Aussi une case se définit donc également par la liste, L, des symboles possibles, i.e. qui ne contredisent pas les contraintes d'unicité ci-dessus.
Une case se définit également par 3 états:
On a attribué définitivement un symbole à la case.
On a attribué temporairement un symbole à la case (uniquement utilisé lors des saisies du joueur).
La case est incertaine, si on ne lui a pas attribué définitivement de symbole.

Lorsque l'on attribue définitivement un symbole à une case, on va en informer les cases de la ligne, colonne et boîte à laquel cette case appartient, et aussi vérifier les contraintes d'unicité.
Alors que lorsque que l'on attribue temporairement un symbole à une case, les contraintes d'unicité ne sont pas vérifiées, mais on prévient aussi les cases de la ligne, colonne et boîte à laquel cette case appartient.

Algorithme de création de grille:
A l'initialisation on créé les cases, les lignes, les colonnes et les boîtes, et on asssocie à chaque case la ligne, la colonne et la boîte à laquelle elle appartient et réciproquement.
Aussi au départ lorsque la grille est vide, la liste L de chaque case est égale à V.
Tant que toute les case ne sont pas à l'état "attribuer définitivement":
On choisit la case, C, dont le nombre de symboles possibles est le plus faible et dont l'état est incertain et on lui attribue définitivement un symbole au hasard, S, choisi parmi la liste L.
En conséquence, on prévient les autres cases, i.e. la ligne, la colonne, et la boîte de cette case vont retirer le symbole S de la liste L de chacune de leurs cases à l'état incertain:
Si les contraintes d'unicité ne sont pas vérifiés, i.e. si une liste, L, de ces cases ne comporte plus de symbole, i.e. L est vide, cele signifie que la grille ne possède pas de solution:
On désattribue S à C et on rétabli la précédente liste L de chacune des cases à l'état incertain de la ligne , colonne et boîte concernée.
Si S était le dernier symbole possible de L, on revient à la case précédente.
Sinon, on réattribue définitivement à C un autre symbole S parmi L.
Sinon on rechoisit une case, C, dont le nombre de symboles possibles est le plus faible et dont l'état est incertain et on lui attribue définitivement un symbole au hasard, S, choisi parmi la liste L.
etc...
Fin tant que
Une fois la gille remplie, on définit un nombre, P, de cases, déterminé par la difficulté du Sudoku choisi.
Tant qu'il n'y a pas P cases à l'état incertain
on choisit une case, C, au hasard, et on désattribue son symbole S.
Fin tant que

Algorithme de résolution:
C'est exactement le même que celui de création sauf que la grille posséde un état initiale différent:

A l'initialisation on créé les cases, les lignes, les colonnes et les boîtes, et on asssocie à chaque case la ligne, la colonne et la boîte à laquelle elle appartient et réciproquement.
Aussi au départ lorsque la grille est vide, la liste L de chaque case est égale à V.

On attribue définitivement à chaque case déja remplie le symbole prédéterminé.

Tant que toute les case ne sont pas à l'état "attribuer définitivement":
On choisit la case, C, dont le nombre de symboles possibles est le plus faible et dont l'état est incertain et on lui attribue définitivement un symbole au hasard, S, choisi parmi la liste L.
En conséquence, on prévient les autres cases, i.e. la ligne, la colonne, et la boîte de cette case vont retirer le symbole S de la liste L de chacune de leurs cases à l'état incertain:
Si les contraintes d'unicité ne sont pas vérifiés, i.e. si une liste, L, de ces cases ne comporte plus de symbole, i.e. L est vide, cele signifie que la grille ne possède pas de solution:
On désattribue S à C et on rétabli la précédente liste L de chacune des cases à l'état incertain de la ligne , colonne et boîte concernée.
Si S était le dernier symbole possible de L, on revient à la case précédente.
Sinon, on réattribue définitivement à C un autre symbole S parmi L.
Sinon on rechoisit une case, C, dont le nombre de symboles possibles est le plus faible et dont l'état est incertain et on lui attribue définitivement un symbole au hasard, S, choisi parmi la liste L.
etc...
Fin tant que

Les avantages de cet algo:
On ne recalcule pas sytèmatiquement pour chaque case la liste des symboles possibles: on lit un tableau => gain de temps
On ne teste pas, pour chaque case, L'ensemble des symboles (V), on ne teste que les symboles possibles (L) vérifiant déja les contraintes d'unicités définie par l'attribution des cases précédentes => gain de temps.
Lors de la résoltuion, on ne choisi pas les cases "au hasard", ou dans un orde établi, on prend celle qui statistiquemet posséde le moins de possibilités.
Les contraintes de cet algo:
Necessite des ressources en mémoire: car  avant de la modifier, on sauvegarde systématiquement la liste, L, des symboles possibles des cases concernées par l'attribution d'une autre case.
 

Source

  • <?php
  • /**
  • * @desc Generateur et solveur de grille de Sudoku *
  • * @author Nifhell
  • * @package sudoku
  • * @version 1.0
  • */
  • include_once($_SERVER['DOCUMENT_ROOT']."cobject.php");
  • /**
  • * @desc Classe CSudoku
  • *
  • */
  • class CSuDoKu extends CObject {
  • private $m_oDebug;
  • private static $m_oInstance;
  • protected $m_iNb;
  • protected $m_sType;
  • protected $m_aLines;
  • protected $m_aCols;
  • protected $m_aCases;
  • protected $m_aSquare;
  • protected $m_iLevel;
  • protected $m_iCache;
  • protected $m_bTutorial=false;
  • protected $m_iTime;
  • protected $m_iIter=-1;
  • /**
  • * @desc Constructeur par valeur
  • * @return CSuDoKu
  • * @param int $nb
  • * @param string $type
  • * @param int $level
  • * @param bool $tuto
  • **/
  • public function CSuDoKu() {
  • //$this->m_oDebug=CDebug::getInstance(false,true);
  • $start=microtime(true);
  • if(func_num_args()>1 and func_num_args()<=4) {
  • func_get_arg(0)?$nb=func_get_arg(0):$nb=9;
  • func_get_arg(1)?$type=func_get_arg(1):$type='numeric';
  • func_get_arg(2)?$level=func_get_arg(2):$level=4;
  • func_get_arg(3)?$tuto=func_get_arg(3):$tuto=false;
  • $this->m_bTutorial=$tuto;
  • $this->CSuDoKuGrille($nb,$type);
  • $this->CSuDoKuLevel($level);
  • }
  • elseif (func_num_args()==1 and $param=func_get_arg(0)) {
  • $this->XML2CSuDoKu($param);
  • }
  • $this->m_iTime=microtime(true)-$start;
  • //$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","Constructor","($this->m_iNb,$this->m_sType,$this->m_iLevel,$this->m_bTutorial)","Sudoku initialisé: ".$this->m_iTime."s");
  • }
  • /**
  • * @desc Détermine le nombre de cases a cacher
  • * @return CSuDoKu
  • * @param int $nb
  • * @param string $type
  • **/
  • private function CSuDoKuGrille($nb,$type) {
  • if(4<=$nb and $nb<=25
  • and intval($sqrt=sqrt($nb))*10==$sqrt*10) {
  • $this->m_iNb=$nb;
  • $this->m_sType=$type;
  • //$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","CSuDoKuGrille","($nb,$type)","Racine: $sqrt.");
  • $this->m_aLines=array();
  • $this->m_aCols=array();
  • $this->m_aCases=array();
  • $this->m_aSquare=array();
  • for($i=0;$i<$nb;$i++) {
  • if(!($this->m_aLines[$i] instanceof CSuDoKuLine)){
  • $this->m_aLines[$i]=new CSuDoKuLine($i);
  • }
  • if(!($i%$sqrt)) {
  • $this->m_aSquare[$i/$sqrt]=array();
  • }
  • $this->m_aCases[$i]=array();
  • for($j=0;$j<$nb;$j++) {
  • if(!($this->m_aCols[$j] instanceof CSuDoKuColumn)){
  • $this->m_aCols[$j]=new CSuDoKuColumn($j);
  • }
  • $this->m_aCases[$i][$j]=new CSuDoKuCase($i,$j,$nb,$type);
  • $this->m_aLines[$i]->addCase($this->m_aCases[$i][$j]);
  • $this->m_aCols[$j]->addCase($this->m_aCases[$i][$j]);
  • if(!($j%$sqrt) and !($i%$sqrt)) {
  • $this->m_aSquare[$i/$sqrt][$j/$sqrt]=new CSuDoKuSquare($i/$sqrt,$j/$sqrt);
  • }
  • $this->m_aSquare[intval($i/$sqrt)][intval($j/$sqrt)]->addCase($this->m_aCases[$i][$j],$i-($sqrt*intval($i/$sqrt)),$j-($sqrt*intval($j/$sqrt)));
  • $this->m_aCases[$i][$j]->square($this->m_aSquare[intval($i/$sqrt)][intval($j/$sqrt)]);
  • $this->m_aCases[$i][$j]->line($this->m_aLines[$i]);
  • $this->m_aCases[$i][$j]->column($this->m_aCols[$j]);
  • }
  • }
  • }
  • return $this;
  • }
  • /**
  • * @desc Détermine le nombre de cases a cacher
  • * @return CSuDoKu
  • * @param int $level
  • **/
  • public function CSuDoKuLevel($level) {
  • switch ($level){
  • case 1:
  • $this->m_iLevel=1;
  • $base=mt_rand(30,35);
  • break;
  • case 2:
  • $this->m_iLevel=2;
  • $base=mt_rand(36,41);
  • break;
  • case 3:
  • $this->m_iLevel=3;
  • $base=mt_rand(42,47);
  • break;
  • case 4:
  • $this->m_iLevel=4;
  • $base=mt_rand(48,53);
  • break;
  • case 5:
  • $this->m_iLevel=5;
  • $base=mt_rand(54,59);
  • break;
  • case 6:
  • $this->m_iLevel=6;
  • $base=mt_rand(60,65);
  • break;
  • case 7:
  • $this->m_iLevel=7;
  • $base=mt_rand(66,71);
  • break;
  • default:
  • $this->m_iLevel=4;
  • $base=mt_rand(48,53);
  • }
  • $this->m_iCache=intval($base/100*pow($this->m_iNb,2));
  • //$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","CSuDoKuLevel","($level)","Niveau: $this->m_iLevel, Caches: $this->m_iCache");
  • return $this;
  • }
  • /**
  • * @desc XML => CSuDoKu
  • * @return CSuDoKu
  • * @param string $xml_source
  • **/
  • private function XML2CSuDoKu($xml_source) {
  • if(isset($xml_source)){
  • if(!is_file($xml_source)){
  • $xml=simplexml_load_string($xml_source);
  • $this->CSuDoKu((int)$xml['nbr'],(string)$xml['type'],(int)$xml['level'],($xml['tuto']=='true'?true:false));
  • foreach ($xml->cell as $cell) {
  • $case=$this->m_aCases[(int)$cell['x']][(int)$cell['y']];
  • if((string)$cell) $case->attribuer((string)$cell);
  • if((string)$cell['test']) $case->test((string)$cell['test']);
  • }
  • ////$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","XML2SuDoKu","($path)","XML => Chargement OK");
  • return $this;
  • }
  • elseif(is_file($xml_source)) {
  • $xml=simplexml_load_file($xml_source);
  • $this->CSuDoKu((int)$xml['nbr'],(string)$xml['type'],(int)$xml['level'],($xml['tuto']=='true'?true:false));
  • foreach ($xml->cell as $cell) {
  • $case=$this->m_aCases[(int)$cell['x']][(int)$cell['y']];
  • if((string)$cell) $case->attribuer((string)$cell);
  • if((string)$cell['test']) $case->test((string)$cell['test']);
  • }
  • ////$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","XML2SuDoKu","($path)","XML => Chargement OK");
  • return $this;
  • }
  • }
  • else {
  • //$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","XML2SuDoKu","($path)","XML => Chargement ECHEC (Le fichier n'existe pas)");
  • return null;
  • }
  • }
  • /**
  • * @desc Cache les cases de la grille de SuDoKu en cours
  • * @return bool
  • * @param void
  • **/
  • private function cache() {
  • for($i=0;$i<$this->m_iCache;$i++) {
  • do {
  • $x=mt_rand(0,$this->m_iNb-1);
  • $y=mt_rand(0,$this->m_iNb-1);
  • $case=$this->m_aCases[$x][$y];
  • }while(!$case->isDone());
  • $case->reSet();
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","cache","($i)",$case->__toString()." cachée");
  • }
  • for($x=0;$x<$this->m_iNb;$x++) {
  • for($y=0;$y<$this->m_iNb;$y++) {
  • $case=$this->m_aCases[$x][$y];
  • if($case->isDone()) {
  • $case->attribuer($case->getSymbols(0));
  • }
  • }
  • }
  • }
  • /**
  • * @desc Résoud la grille de SuDoKu en cours
  • * @return bool
  • * @param int $i
  • **/
  • private function resoudre($i) {
  • $tab="";
  • $this->m_iIter++;
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$this->toStrTable());
  • if(is_array($coord=$this->best2solve())) {
  • $case=$this->m_aCases[$coord['x']][$coord['y']];
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab.$case->__toString()." choisie.");
  • foreach($case->getSymbols() as $s) {
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."Symboles possibles de la ".$case->__toString().": ".join(", ",$case->getSymbols()));
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."\t Test du symbole $s: ");
  • if($case->attribuer($s)) {
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."\t $s valider provisoirement.");
  • if($this->resoudre($i+1)){
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."\t $s enterriner.");
  • return true;
  • }
  • else {
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."\t $s ne convient pas au case d'ordre inférieur => on réinitialise la case symbole suivant.");
  • $case->desattribuer();
  • }
  • }
  • else {
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."\t $s ne convient au case de la même ligne, colonne ou boite => symbole suivant.");
  • $case->desattribuer();
  • }
  • }
  • return false;
  • }
  • else return true;
  • }
  • /**
  • * @desc Renvoie les coordonnées de la case qui a le moins de possibilité
  • * @return array
  • * @param
  • **/
  • public function best2solve() {
  • $min=$this->m_iNb+1;
  • $min_x=$min_y=-1;
  • for($x=0;$x<$this->m_iNb;$x++) {
  • for($y=0;$y<$this->m_iNb;$y++) {
  • if(!$this->m_aCases[$x][$y]->isDone()){
  • $tmp=count($this->m_aCases[$x][$y]->getSymbols());
  • if($tmp<$min) {
  • $min=$tmp;
  • $min_x=$x;
  • $min_y=$y;
  • }
  • }
  • }
  • }
  • if($min>0 and $min!=$this->m_iNb+1) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKu","best2solve","()","Case best2solve: ".$this->m_aCases[$min_x][$min_y]->__toString()." minimum => $min.");
  • return array('x' => $min_x, 'y' => $min_y, 0 => $min_x, 1 => $min_y);
  • }
  • else {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKu","best2solve","()","Case best2solve: ".($this->m_aCases[$min_x][$min_y]?$this->m_aCases[$min_x][$min_y]->__toString():"")." | minimum => $min");
  • return null;
  • }
  • }
  • /**
  • * @desc Génere une grille
  • * @return CSuDoKu
  • * @param
  • **/
  • public function genererGrille() {
  • $start=microtime(true);
  • $this->reSet();
  • $bOK=$this->resoudre(0);
  • $bOK=$bOK*$this->isValid();
  • $this->cache();
  • $bOK=$bOK*$this->isValid();
  • $this->m_iTime=microtime(true)-$start;
  • //$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","genererGrille","()","Grille initialisé (".$this->m_iTime."s): ".($bOK?"OK":"ECHEC"));
  • return $bOK;
  • }
  • /**
  • * @desc Résoud une grille non résolu
  • * @return CSuDoKu
  • * @param
  • **/
  • public function resoudreGrille() {
  • $start=microtime(true);
  • $bOK=$this->resoudre(0);
  • $bOK=$bOK*$this->isValid();
  • $this->m_iTime=microtime(true)-$start;
  • //$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","resoudreGrille","()","Grille résolue (".$this->m_iTime."s): ".($bOK?"OK":"ECHEC"));
  • return $bOK;
  • }
  • /**
  • * @desc Renvoie le nombre d'itération que l'algo de résolution a effectuer
  • * @return int
  • * @param
  • **/
  • public function getIter() {
  • return $this->m_iIter-pow($this->m_iNb,2);
  • }
  • /**
  • * @desc Renvoie le nombre de case par ligne
  • * @return int
  • * @param
  • **/
  • public function getNbr() {
  • return $this->m_iNb;
  • }
  • /**
  • * @desc Renvoie le nombre de case par ligne
  • * @return int
  • * @param
  • **/
  • public function getLevel() {
  • return $this->m_iLevel;
  • }
  • /**
  • * @desc Résoud une grille non résolu
  • * @return CSuDoKu
  • * @param string $mode
  • **/
  • public function getTime($mode) {
  • switch($mode){
  • case 's':
  • return round($this->m_iTime,2);
  • break;
  • case 'cs':
  • return round($this->m_iTime*10,2);
  • break;
  • case 'ms':
  • return round($this->m_iTime*100,2);
  • break;
  • case '_s':
  • return round($this->m_iTime*1000,2);
  • break;
  • default:
  • return round($this->m_iTime,2);
  • }
  • }
  • /**
  • * @desc Vide la grille.
  • * Si $shuffle est à vrai, alors le prochain appel de résoudre(), générera une nouvelle grille (par défaut)
  • * Si $shuffle est à faux, alors la grille est simplement vidé, mais le prochain appel de resoudre() générera la même grille que précédemment. Pratique, si vous souhiatez avoir la même grille mais avec des niveaux de difficulté différents.
  • * @return CSuDoKu
  • * @param bool $shuffle = true
  • **/
  • public function reSet($shuffle=true) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKu","reSet","()","initialisation");
  • $this->m_iIter=-1;
  • for($i=0;$i<count($this->m_aCases);$i++){
  • for($j=0;$j<count($this->m_aCases[$i]);$j++){
  • $this->m_aCases[$i][$j]->reSet($shuffle);
  • }
  • }
  • return $this;
  • }
  • /**
  • * @desc (Static) Renvoie l'instance en cours de la classe CSuDoKu
  • * @return CSuDoKu
  • * @param int $nb
  • * @param string $type
  • * @param int $level
  • * @param bool $tuto
  • **/
  • public static function getInstance($nb,$type, $level, $tuto) {
  • if (empty(self::$m_oInstance)) {
  • self::$m_oInstance = new CSuDoKu($nb,$type, $level, $tuto);
  • }
  • return self::$m_oInstance;
  • }
  • /**
  • * @desc CSuDoKu => SimpleXMLElement
  • * @return SimpleXMLElement
  • * @param string $path
  • **/
  • public function toXML($path=false) {
  • $xml_string="<?xml version=\"1.0\" standalone=\"yes\"?>\n";
  • $xml_string.="<sudoku type=\"".$this->m_sType."\" nbr=\"".$this->m_iNb."\" level=\"".$this->m_iLevel."\" tuto=\"".($this->m_bTutorial?'true':'false')."\">\n";
  • for($i=0;$i<$this->m_iNb;$i++){
  • for($j=0;$j<$this->m_iNb;$j++){
  • if($this->getCase($i,$j)->getTest()) $test=$this->getCase($i,$j)->getTest();
  • else $test="";
  • $xml_string.="<cell x=\"".$i."\" y=\"".$j."\" test=\"".$test."\">";
  • if($this->m_aCases[$i][$j]->isDone()) $xml_string.=$this->m_aCases[$i][$j]->getSymbols(0);
  • $xml_string.="</cell>\n";
  • }
  • }
  • $xml_string.="</sudoku>";
  • //$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","toXML","($path)","Chargement de la grille: ".$xml_string);
  • $xml=simplexml_load_string($xml_string);
  • if((!isset($path) and $path!=false) or !$xml->asXML($path)) {
  • //$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","toXML","($path)","XML => Enregistrement du fichier XML ECHEC");
  • }
  • return $xml_string;
  • }
  • /**
  • * @desc Transforme le SuDoKu en Table HTML
  • * @return string
  • * @param
  • **/
  • public function toHTMLTable() {
  • $table="<table>\n\t";
  • for($i=1;$i<=$this->m_iNb;$i++){
  • $table.="<tr>";
  • for($j=1;$j<=$this->m_iNb;$j++){
  • $absc=$i-1;
  • $ord=$j-1;
  • $symbols=$this->getCase($absc,$ord)->getSymbols();
  • $table.="<td>";
  • if($this->getCase($absc,$ord)->isDone()){
  • $table.=$symbols[0];
  • }
  • else {
  • if($this->m_bTutorial) {
  • $table.="<select name=\"case_".$i."_".$j."\" size=1>";
  • $table.="<option value=\"null\">";
  • natcasesort($symbols);
  • foreach ($symbols as $sym) {
  • $b="";
  • if($this->getCase($absc,$ord)->getTest()) {
  • if($sym==$this->getCase($absc,$ord)->getTest()) $b="selected";
  • }
  • $table.="<option value=\"".$sym."\" ".$b." >".$sym."";
  • }
  • }
  • else {
  • if($this->getCase($absc,$ord)->getTest()) $default=$this->getCase($absc,$ord)->getTest();
  • else $default="";
  • $table.="<input type=\"text\" name=\"case_".$i."_".$j."\" size=\"1\" maxlength=\"1\" onchange=\"check_sudoku_input(this)\" value=\"".$default."\">";
  • }
  • }
  • $table.="</td>";
  • }
  • $table.="</tr>\n";
  • }
  • $table.="</table>\n";
  • $table.="<script type=\"Javascript\">";
  • $table.="function check_sudoku_input(nCase) {
  • var Symbols=/^\s|".join("|",$this->getCase(0,0)->getOriginalSymbols())."$/;
  • var bOk=false;
  • if(nCase.value && !Symbols.test(nCase.value)) {
  • alert('Arrrg! Vous devez saisir un caractère parmi ceux-ci: ".join(", ",$this->getCase(0,0)->getOriginalSymbols())."');
  • nCase.value='';
  • nCase.focus();
  • }
  • return bOk;
  • }
  • </script>\n";
  • $this->setHTML($table);
  • return $table;
  • }
  • /**
  • * @desc Transforme le SuDoKu en Table
  • * @return string
  • * @param
  • **/
  • public function toStrTable() {
  • $str="\n";
  • for($i=0;$i<$this->m_iNb;$i++){
  • if (!($i%sqrt($this->m_iNb))){
  • for($j=1;$j<=$this->m_iNb;$j++){
  • $str.="\t-";
  • }
  • $str.="\n";
  • }
  • for($j=0;$j<$this->m_iNb;$j++){
  • if(!($j%sqrt($this->m_iNb))) {
  • $str.="\t|";
  • }
  • $symbols=$this->getCase($i,$j)->getSymbols();
  • if($this->getCase($i,$j)->isDone()){
  • $str.="\t".$symbols[0];
  • }
  • else {
  • $str.="\t ";
  • }
  • }
  • $str.="\n";
  • }
  • return $str;
  • }
  • /**
  • * @desc Transforme le SuDoKu en tableau HTML
  • * @return string
  • * @param void
  • **/
  • public function __toString() {
  • return $this->toHTMLTable();
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuCase
  • * @param int $i
  • * @param int $j
  • **/
  • public function getCase($i,$j) {
  • if($i<$this->m_iNb and $j<$this->m_iNb){
  • return $this->m_aCases[$i][$j];
  • }
  • else {
  • echo "<hr>Fatal error: CSudoku:getCase($i,$j):: out of range";
  • return null;
  • }
  • }
  • /**
  • * @desc Vérifie si le mod tutoriel est activé
  • * @return bool
  • * @param
  • **/
  • public function isEasy() {
  • return $this->m_bTutorial;
  • }
  • /**
  • * @desc Vérifie les contraintes d'unicité
  • * @return bool
  • * @param
  • **/
  • public function isValid() {
  • $bRet=array();
  • for($x=0;$x<$this->m_iNb;$x++) {
  • for($y=0;$y<$this->m_iNb;$y++) {
  • if($this->m_aCases[$x][$y]->isEmpty()){
  • $bRet[]=false;
  • break;
  • }
  • }
  • }
  • foreach ($this->m_aLines as $obj) {
  • $bRet[]=$obj->isValid();
  • }
  • foreach ($this->m_aCols as $obj) {
  • $bRet[]=$obj->isValid();
  • }
  • foreach ($this->m_aSquare as $tmp) {
  • foreach ($tmp as $obj) {
  • $bRet[]=$obj->isValid();
  • }
  • }
  • if(in_array(false,$bRet)) {
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","isValid","","Cette grille est fausse.");
  • return false;
  • }
  • else {
  • //$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","isValid","","Cette grille est valide.");
  • return true;
  • }
  • }
  • }
  • class CSuDoKuLine extends CObject {
  • private $m_oDebug;
  • protected $m_iOrd;
  • protected $m_aCases;
  • /**
  • * @desc Constructeur par défaut
  • * @return CSuDoKuLine
  • * @param int $i
  • **/
  • public function CSuDoKuLine($i) {
  • //$this->m_oDebug=CDebug::getInstance(false,true);
  • if(intval($i)*10==$i*10){
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuLine","Constructor","($i)","$this");
  • $this->m_iOrd=$i;
  • }
  • }
  • /**
  • * @desc Add a case
  • * @return CSuDoKuLine
  • * @param CSuDoKuCase $case
  • **/
  • public function addCase(CSuDoKuCase $case) {
  • if($case instanceof CSuDoKuCase) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuLine","addCase","($case)","Ligne($this->m_iOrd)<=Case: (".$case->getAbsc().",".$case->getOrd().")");
  • $this->m_aCases[]=$case;
  • return $this;
  • }
  • else {
  • echo "<hr>Fatal error: CSuDoKuLine::addCase(\$case) shouldn't be empty.";
  • return null;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return array
  • * @param
  • **/
  • public function getCases() {
  • return $this->m_aCases;
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuCase
  • * @param int $i
  • **/
  • public function getCase($i) {
  • if($i>=0 and $i<count($this->m_aCases)) {
  • return $this->m_aCases[$i];
  • }
  • else {
  • echo "<hr>Fatal Error: \$i out of range.";
  • return null;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return int
  • * @param
  • **/
  • public function getOrd() {
  • return $this->m_iOrd;
  • }
  • /**
  • * @desc Vérifie les contraintes d'unicité
  • * @return bool
  • * @param
  • **/
  • public function isValid() {
  • $bRet=true;
  • $tmp=array();
  • foreach ($this->m_aCases as $case) {
  • if($case->isDone()) $tmp[]=$case;
  • }
  • foreach ($tmp as $case1) {
  • $sym1=$case1->getSymbols();
  • foreach ($tmp as $case2) {
  • $sym2=$case2->getSymbols();
  • if(($sym1[0]==$sym2[0] or !strcasecmp($sym1[0],$sym2[0])) and strcasecmp($case1->__toString(),$case2->__toString())) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSudoKuLine","isValid","","Violation de contrainte d'unicité de la ligne ".$this->m_iOrd.": ".$case1->__toString()."=".$case2->__toString()."=".$sym1[0]);
  • $bRet=false;
  • }
  • }
  • }
  • return $bRet;
  • }
  • /**
  • * @desc Propage le fait qu'une case a été découverte
  • * @return CSuDoKuLine
  • * @param mixed $symbole
  • * @param CSuDoKuCase $cCase
  • **/
  • public function propagate($symbole,CSuDoKuCase $cCase) {
  • $bOK=true;
  • foreach ($this->m_aCases as $case) {
  • if(!$case->isDone() and $case!==$cCase and $case->getSquare()!==$cCase->getSquare()){
  • $case->delSymbols($symbole,true,$cCase->getObjectID());
  • if($case->isEmpty()) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuLine","propagate","($symbole,$cCase)",$this->__toString().": ".$case->__toString()." est vide.");
  • $bOK=false;
  • }
  • }
  • }
  • //$bOK?//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuLine","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation réussie.")://$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuLine","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation échouée.");
  • return $bOK;
  • }
  • /**
  • * @desc déPropage le fait qu'une case n'est plus découverte
  • * @return CSuDoKuLine
  • * @param CSuDoKuCase $cCase
  • **/
  • public function unpropagate(CSuDoKuCase $cCase) {
  • ////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuLine","unpropagate","(".$cCase->__toString().")",$this->__toString().": dépropagation");
  • foreach ($this->m_aCases as $case) {
  • if($case!==$cCase and $case->getSquare()!==$cCase->getSquare()){
  • if(!$case->isDone()) {
  • $case->restaurer($cCase->getObjectID());
  • }
  • }
  • }
  • return $this;
  • }
  • /**
  • * @desc toString
  • * @return string
  • * @param
  • **/
  • public function __toString() {
  • return "Ligne #".$this->m_iOrd;
  • }
  • }
  • class CSuDoKuColumn extends CObject {
  • private $m_oDebug;
  • protected $m_iAbsc;
  • protected $m_aCases;
  • /**
  • * @desc Constructeur par défaut
  • * @return CSuDoKuColumn
  • * @param int $i
  • **/
  • public function CSuDoKuColumn($i) {
  • //$this->m_oDebug=CDebug::getInstance(false,true);
  • if(intval($i)*10==$i*10){
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuColumn","Constructor","($i)","$this");
  • $this->m_iAbsc=$i;
  • }
  • }
  • /**
  • * @desc Add a case
  • * @return CSuDoKuColumn
  • * @param CSuDoKuCase $case
  • **/
  • public function addCase(CSuDoKuCase $case) {
  • if($case instanceof CSuDoKuCase) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuColumn","addCase","($case)","Colonne($this->m_iAbsc)<=Case: (".$case->getAbsc().",".$case->getOrd().")");
  • $this->m_aCases[]=$case;
  • return $this;
  • }
  • else {
  • echo "<hr>Fatal error: CSuDoKuColumn::addCase(\$case) shouldn't be empty.";
  • return null;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuCase
  • * @param int $i
  • **/
  • public function getCase($i) {
  • if($i>=0 and $i<count($this->m_aCases)) {
  • return $this->m_aCases[$i];
  • }
  • else {
  • echo "<hr>Fatal Error: \$i out of range.";
  • return null;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return array
  • * @param
  • **/
  • public function getCases() {
  • return $this->m_aCases;
  • }
  • /**
  • * @desc Accesseur
  • * @return int
  • * @param void
  • **/
  • public function getAbsc() {
  • return $this->m_iAbsc;
  • }
  • /**
  • * @desc Accesseur
  • * @return bool
  • * @param void
  • **/
  • public function isValid() {
  • $bRet=true;
  • $tmp=array();
  • foreach ($this->m_aCases as $case) {
  • if($case->isDone()) $tmp[]=$case;
  • }
  • foreach ($tmp as $case1) {
  • $sym1=$case1->getSymbols();
  • foreach ($tmp as $case2) {
  • $sym2=$case2->getSymbols();
  • if(($sym1[0]==$sym2[0] or !strcasecmp($sym1[0],$sym2[0])) and strcasecmp($case1->__toString(),$case2->__toString())) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuColumn","isValid","","Violation de contrainte d'unicité de la Colonne ".$this->m_iAbsc.": ".$case1->__toString()."=".$case2->__toString()."=".$sym1[0]);
  • $bRet=false;
  • }
  • }
  • }
  • return $bRet;
  • }
  • /**
  • * @desc Propage le fait qu'une case a été découverte
  • * @return CSuDoKuColumn
  • * @param mixed $symbole
  • * @param CSuDoKuCase $cCase
  • **/
  • public function propagate($symbole,CSuDoKuCase $cCase) {
  • $bOK=true;
  • foreach ($this->m_aCases as $case) {
  • if(!$case->isDone() and $case!==$cCase and $case->getSquare()!==$cCase->getSquare()) {
  • $case->delSymbols($symbole,true,$cCase->getObjectID());
  • if($case->isEmpty()) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuColumn","propagate","($symbole,$absc,$ord)",$this->__toString().": ".$case->__toString()." est vide");
  • $bOK=false;
  • }
  • }
  • }
  • //$bOK?//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuColumn","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation réussie.($bOK)")://$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuColumn","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation échouée.");
  • return $bOK;
  • }
  • /**
  • * @desc déPropage le fait qu'une case a été remis à l'état incertain
  • * @return CSuDoKuColumn
  • * @param CSuDoKuCase $cCase
  • **/
  • public function unpropagate(CSuDoKuCase $cCase) {
  • ////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuColumn","unpropagate","(".$cCase->__toString().")",$this->__toString().": dépropagation");
  • foreach ($this->m_aCases as $case) {
  • if($case!==$cCase and $case->getSquare()!==$cCase->getSquare()){
  • if(!$case->isDone()) {
  • $case->restaurer($cCase->getObjectID());
  • }
  • }
  • }
  • return $this;
  • }
  • /**
  • * @desc toString
  • * @return string
  • * @param void
  • **/
  • public function __toString() {
  • return "Colonne #".$this->m_iAbsc;
  • }
  • }
  • class CSuDoKuSquare extends CObject {
  • private $m_oDebug;
  • protected $m_iAbsc;
  • protected $m_iOrd;
  • protected $m_aCases;
  • /**
  • * @desc Constructeur par défaut
  • * @return CSuDoKuSquare
  • * @param int $i
  • * @param int $j
  • **/
  • public function CSuDoKuSquare($i,$j) {
  • //$this->m_oDebug=CDebug::getInstance(false,true);
  • if(intval($i)*10==$i*10 and intval($j)*10==$j*10){
  • $this->m_iAbsc=$i;
  • $this->m_iOrd=$j;
  • $this->m_aCases=array();
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuSquare","Constructor","($i,$j)","$this");
  • }
  • else echo "<hr>Fatal error: CSuDoKuSquare(\$i,\$j,\$sym) shouldn't be empty.";
  • }
  • /**
  • * @desc Add a case
  • * @return CSuDoKuSquare
  • * @param CSuDoKuCase $case
  • * @param int $i
  • * @param int $j
  • **/
  • public function addCase(CSuDoKuCase $case,$i,$j) {
  • if($case instanceof CSuDoKuCase) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuSquare","addCase","($case,$i,$j)","Carré(".$this->m_iAbsc.",".$this->m_iOrd.")<=Case: (".$case->getAbsc().",".$case->getOrd().")");
  • if(!is_array($this->m_aCases[$i])) $this->m_aCases[$i]=array();
  • $this->m_aCases[$i][$j]=$case;
  • return $this;
  • }
  • else {
  • echo "<hr>Fatal error: CSuDoKuSquare::addCase(\$case) shouldn't be empty.";
  • return null;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuCase
  • * @param int $i
  • * @param int $j
  • **/
  • public function getCase($i,$j) {
  • if($i>=0 and $i<count($this->m_aCases) and $j>=0 and $j<count($this->m_aCases[$i])) {
  • return $this->m_aCases[$i][$j];
  • }
  • else {
  • echo "<hr>Fatal Error: \$i or \$j out of range.";
  • return null;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return array
  • * @param void
  • **/
  • public function getCases() {
  • return $this->m_aCases;
  • }
  • /**
  • * @desc Accesseur
  • * @return int
  • * @param void
  • **/
  • public function getAbsc() {
  • return $this->m_iAbsc;
  • }
  • /**
  • * @desc Accesseur
  • * @return int
  • * @param void
  • **/
  • public function getOrd() {
  • return $this->m_iOrd;
  • }
  • /**
  • * @desc Accesseur
  • * @return string
  • * @param void
  • **/
  • public function __toString() {
  • return "Carre[".$this->getObjectID()."](".$this->m_iAbsc.",".$this->m_iOrd.")";
  • }
  • /**
  • * @desc Accesseur
  • * @return bool
  • * @param void
  • **/
  • public function isValid() {
  • $bRet=true;
  • $tmp=array();
  • foreach ($this->m_aCases as $col) {
  • foreach ($col as $case) {
  • if($case->isDone()) $tmp[]=$case;
  • }
  • }
  • foreach ($tmp as $col1) {
  • foreach ($col1 as $case1) {
  • $sym1=$case1->getSymbols();
  • foreach ($tmp as $col2) {
  • foreach ($col2 as $case2) {
  • $sym2=$case2->getSymbols();
  • if(($sym1[0]==$sym2[0] or !strcasecmp($sym1[0],$sym2[0])) and strcasecmp($case1->__toString(),$case2->__toString())) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuSquare","isValid","","Violation de contrainte d'unicité du Carré ".$this->__toString().": ".$case1->__toString()."=".$case2->__toString()."=".$sym1[0]);
  • $bRet=false;
  • }
  • }
  • }
  • }
  • }
  • return $bRet;
  • }
  • /**
  • * @desc Propage le fait qu'une case a été découverte
  • * @return CSuDoKuSquare
  • * @param mixed $symbole
  • * @param CSuDoKuCase $cCase
  • **/
  • public function propagate($symbole,CSuDoKuCase $cCase) {
  • $bOK=true;
  • foreach ($this->m_aCases as $cases) {
  • foreach ($cases as $case) {
  • if(!$case->isDone() and $case!==$cCase){
  • $case->delSymbols($symbole,true,$cCase->getObjectID());
  • if($case->isEmpty()) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuSquare","propagate","($symbole,$cCase)",$this->__toString().": ".$case->__toString()." est vide.");
  • $bOK=false;
  • }
  • }
  • }
  • }
  • //$bOK?//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuSquare","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation réussie.")://$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuSquare","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation échouée.");
  • return $bOK;
  • }
  • /**
  • * @desc déPropage le fait qu'une case a été découverte
  • * @return CSuDoKuSquare
  • * @param CSuDoKuCase $cCase
  • **/
  • public function unpropagate(CSuDoKuCase $cCase) {
  • ////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuSquare","unpropagate","(".$cCase->__toString().")",$this->__toString().": dépropagation");
  • foreach ($this->m_aCases as $cases) {
  • foreach ($cases as $case) {
  • if($case!==$cCase){
  • if(!$case->isDone()) {!
  • $case->restaurer($cCase->getObjectID());
  • }
  • }
  • }
  • }
  • return $this;
  • }
  • }
  • /**
  • * @desc Classe CSuDoKuCase
  • *
  • */
  • class CSuDoKuCase extends CObject {
  • protected $m_oDebug;
  • protected $m_iAbsc;
  • protected $m_iOrd;
  • protected $m_aSaveSymbols;
  • protected $m_aSymbols;
  • protected $m_aOSymbols;
  • protected $m_oSquare;
  • protected $m_oLine;
  • protected $m_oColumn;
  • protected $m_iNb;
  • protected $m_sType;
  • protected $m_bDone;
  • protected $m_sTest=null;
  • /**
  • * @desc Constructeur par défaut
  • * @return CSuDoKuCase
  • * @param int $i
  • * @param int $j
  • * @param int $nb
  • * @param string $type
  • **/
  • public function CSuDoKuCase($i,$j,$nb,$type) {
  • //$this->m_oDebug=CDebug::getInstance(false,true);
  • if(intval($i*10)==$i*10 and intval($j*10)==$j*10 ) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","Constructor","($i,$j,$nb,$type)","$this");
  • $this->m_iAbsc=$i;
  • $this->m_iOrd=$j;
  • $this->m_iNb=$nb;
  • $this->m_bDone=false;
  • $this->m_aSaveSymbols=array();
  • switch($type) {
  • case 'numeric':
  • $this->m_sType='numeric';
  • $this->m_aSymbols=array();
  • for($i=1;$i<=$nb;$i++) {
  • $this->m_aSymbols[]=$i;
  • $this->m_aOSymbols[]=$i;
  • }
  • shuffle($this->m_aSymbols);
  • $this->sauvegarder(-1);
  • break;
  • case 'alpha':
  • $this->m_sType='alpha';
  • $this->m_aSymbols=array();
  • for($i=0;$i<$nb;$i++) {
  • $this->m_aSymbols[]=chr(65+$i);
  • $this->m_aOSymbols[]=chr(65+$i);
  • }
  • shuffle($this->m_aSymbols);
  • $this->sauvegarder(-1);
  • break;
  • default: echo "<hr>Fatal error: CSuDoKuCase(\$type) should be 'numeric' or 'alpha'.";
  • }
  • }
  • else echo "<hr>Fatal error: CSuDoKuCase(\$i, \$j, \$nb,\$type) shouldn't be empty or \$i and \$j should be integers and \$sym should be a CSuDoKuSymboles";
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuCase
  • * @param CSuDoKuSquare $square
  • **/
  • public function square(CSuDoKuSquare $square) {
  • if($square instanceof CSuDoKuSquare ) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","square","($square)","Association de la boîte (".$square->getAbsc().",".$square->getOrd().") à ".$this->__toString());
  • $this->m_oSquare=$square;
  • return $this;
  • }
  • else {
  • echo "<hr>\nCSuDoKuCase::square(\$square)::ERROR:: \$square should be an CSuDoKuSquare object";
  • return null;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuCase
  • * @param CSuDoKuLine $line
  • **/
  • public function line(CSuDoKuLine $line) {
  • if($line instanceof CSuDoKuLine) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","line","($line)","Association de la ligne(".$line->getOrd().") à ".$this->__toString());
  • $this->m_oLine=$line;
  • return $this;
  • }
  • else {
  • echo "<hr>\nCSuDoKuCase::line(\$line)::ERROR:: \$line should be an CSuDoKuLine object";
  • return null;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuCase
  • * @param CSuDoKuColumn $column
  • **/
  • public function column(CSuDoKuColumn $column) {
  • if($column instanceof CSuDoKuColumn) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","column","($column)","Association de la colonne (".$column->getAbsc().") à ".$this->__toString());
  • $this->m_oColumn=$column;
  • return $this;
  • }
  • else {
  • echo "<hr>\nCSuDoKuCase::column(\$column)::ERROR:: \$column should be an CSuDoKuColumn object";
  • return null;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return int
  • * @param void
  • **/
  • public function getRelativeOrd() {
  • return $this->m_iOrd-sqrt($this->m_iNb)*$this->m_oSquare->getOrd();
  • }
  • /**
  • * @desc Accesseur
  • * @return int
  • * @param void
  • **/
  • public function getRelativeAbsc() {
  • return $this->m_iAbsc-sqrt($this->m_iNb)*$this->m_oSquare->getAbsc();
  • }
  • /**
  • * @desc Accesseur
  • * @return int
  • * @param void
  • **/
  • public function getOrd() {
  • return $this->m_iOrd;
  • }
  • /**
  • * @desc Accesseur
  • * @return int
  • * @param void
  • **/
  • public function getAbsc() {
  • return $this->m_iAbsc;
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuLine
  • * @param void
  • **/
  • public function getLine() {
  • return $this->m_oLine;
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuColumn
  • * @param void
  • **/
  • public function getColumn() {
  • return $this->m_oColumn;
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuSquare
  • * @param void
  • **/
  • public function getSquare() {
  • return $this->m_oSquare;
  • }
  • /**
  • * @desc Renvoie string
  • * @return string
  • * @param void
  • **/
  • public function __toString() {
  • return "Case[".$this->getObjectID()."](".$this->m_iAbsc.",".$this->m_iOrd.")";
  • }
  • /**
  • * @desc Sauvegarde les symboles qui ont été modifié par la case dont l'id est $idCase
  • * @return bool
  • * @param int $idCase
  • **/
  • public function sauvegarder($idCase) {
  • if(!$this->isDone() and !$this->isEmpty()){
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","sauvegarder","($idCase)",$this->__toString()."=> sauvegarde n°".$idCase.": (".join(", ",$this->m_aSymbols).")");
  • $this->m_aSaveSymbols[$idCase]=$this->m_aSymbols;
  • }
  • else {
  • if($this->isDone()) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","sauvegarder","($idCase)",$this->__toString()."=> non sauvegarde car découverte");
  • }
  • else {
  • if($this->isEmpty()) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","sauvegarder","($idCase)",$this->__toString()."=> non sauvegarde car vide");
  • }
  • }
  • }
  • }
  • /**
  • * @desc Restaure les symboles
  • * @return bool
  • * @param int $idCase
  • **/
  • public function restaurer($idCase) {
  • if(count($this->m_aSaveSymbols)>0) {
  • if(is_array($this->m_aSaveSymbols[$idCase])) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","restaurer","($idCase)",$this->__toString()."=> restauration de la sauvegarde n°".$idCase.": (".join(", ",$this->m_aSaveSymbols[$idCase]).")");
  • $this->m_aSymbols=$this->m_aSaveSymbols[$idCase];
  • if(count($this->m_aSymbols)>1) $this->m_bDone=false;
  • if(count($this->m_aSaveSymbols)>1) unset($this->m_aSaveSymbols[$idCase]);
  • }
  • else {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","restaurer","($idCase)",$this->__toString()."=> La sauvegarde n°".$idCase." n'existe pas.");
  • }
  • }
  • else {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","restaurer","($idCase)",$this->__toString()."=> pas assez de sauvegarde disponible: ".count($this->m_aSaveSymbols));
  • }
  • }
  • /**
  • * @desc Attribue un symbole
  • * @return bool
  • * @param string $sym
  • **/
  • public function attribuer($sym){
  • if($this->setSymbols($sym,$this->getObjectID())) {
  • $this->m_bDone=true;
  • //$this->m_oDebug->addTrace(3,$this->getObjectID(),"CSuDoKuCase","attribuer","(".$sym.")",$this->__toString()."=> attribue $sym");
  • return $this->propagate();
  • }
  • else {
  • //$this->m_oDebug->addTrace(3,$this->getObjectID(),"CSuDoKuCase","attribuer","(".$sym.")",$this->__toString()."=> setSymbols($sym) a échoué");
  • $this->m_bDone=false;
  • return false;
  • }
  • }
  • /**
  • * @desc DesAttribue un symbole
  • * @return CSuDoKuCase
  • **/
  • public function desattribuer() {
  • //$this->m_oDebug->addTrace(3,$this->getObjectID(),"CSuDoKuCase","desattribuer","()",$this->__toString()."=> désattribue ");
  • $this->m_bDone=false;
  • $this->restaurer($this->getObjectID());
  • return $this->unpropagate();
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKuCase
  • * @param mixed $symboles
  • * @package int $idCase
  • **/
  • public function setSymbols($symboles,$idCase) {
  • $this->sauvegarder($idCase);
  • $bOK=1;
  • if(is_array($symboles)){
  • $symboles=array_unique($symboles);
  • if(count($this->m_aOSymbols)>=count($symboles) and count($symboles)>0) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","setSymbols","(".$symboles.")",$this->__toString().": Modification de la liste des symboles.");
  • $a_enlever=array_diff($this->m_aSymbols,$symboles);
  • $a_ajouter=array_diff($symboles,$this->m_aSymbols);
  • if(count($a_enlever)) {
  • foreach ($a_enlever as $sym) {
  • $bOK=$bOK*$this->delSymbols($sym,false);
  • }
  • }
  • if(count($a_ajouter)) {
  • foreach ($a_ajouter as $sym) {
  • $bOK=$bOK*$this->addSymbols($sym,false);
  • }
  • }
  • }
  • else $bOK=0;
  • }
  • elseif(!is_array($symboles) and in_array($symboles,$this->m_aOSymbols)) {
  • ////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","setSymbols","(".$symboles.")",$this->__toString().": isDone() => true, Symbole=$symboles.");
  • $this->m_aSymbols=array(0 => $symboles);
  • $bOK=1;
  • }
  • else {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","setSymbols","(".join(", ",$symboles).")",$this->__toString().": Le tableau est vide ou contient trop de symboles, ou ce symbole n'existe pas.");
  • $bOK=0;
  • }
  • return $bOK;
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKu
  • * @param mixed $symboles
  • * @param bool $bSave
  • * @param int $idCase
  • **/
  • public function addSymbols($symboles,$bSave,$idCase) {
  • if($bSave) $this->sauvegarder($idCase);
  • $bOK=1;
  • if(is_array($symboles)){
  • $symboles=array_unique($symboles);
  • if(count($symboles)>0) {
  • $a_ajouter=array_diff($symboles,$this->m_aSymbols);
  • if(count($a_ajouter)>0) {
  • foreach ($a_ajouter as $sym) {
  • $bOK=$bOK*$this->addSymbols($sym,false);
  • }
  • }
  • }
  • else $bOK=0;
  • }
  • elseif(!is_array($symboles) and in_array($symboles,$this->m_aSymbols)) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","addSymbols","(".$symboles.")",$this->__toString().": Ce symbole existe déja.");
  • $bOK=0;
  • }
  • elseif(!is_array($symboles) and !in_array($symboles,$this->m_aSymbols) and in_array($symboles,$this->m_aOSymbols)) {
  • $this->m_aSymbols[]=$symboles;
  • //shuffle($this->m_aSymbols);
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","addSymbols","(".$symboles.")",$this->__toString().": Ajout de $symboles:".join(", ",$this->m_aSymbols));
  • $bOK=1;
  • }
  • else {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","addSymbols","(".$symboles.")",$this->__toString().": Ce symbole en dehors de tous les cas.");
  • $bOK=0;
  • }
  • return $bOK;
  • }
  • /**
  • * @desc Accesseur
  • * @return CSuDoKu
  • * @param mixed $symboles
  • * @param bool $bSave
  • * @param int $idCase
  • **/
  • public function delSymbols($symboles,$bSave,$idCase) {
  • if($bSave) $this->sauvegarder($idCase);
  • $bOK=1;
  • if(is_array($symboles)) {
  • $symboles=array_unique($symboles);
  • if(count($symboles)>0) {
  • $a_enlever=array_diff($this->m_aSymbols,$symboles);
  • if(count($a_enlever)>0) {
  • foreach ($a_enlever as $sym) {
  • $bOK=$bOK*$this->delSymbols($sym,false);
  • }
  • }
  • }
  • else {
  • $bOK=0;
  • }
  • }
  • elseif(!is_array($symboles) and !in_array($symboles,$this->m_aSymbols)) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","delSymbols","(".$symboles.")",$this->__toString().": Ce symbole n'existe déja plus.");
  • $bOK=0;
  • }
  • elseif(!is_array($symboles) and in_array($symboles,$this->m_aSymbols)) {
  • unset($this->m_aSymbols[array_search($symboles,$this->m_aSymbols)]);
  • //shuffle($this->m_aSymbols);
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","delSymbols","(".$symboles.")",$this->__toString().": Suppression de $symboles:".join(", ",$this->m_aSymbols));
  • $bOK=1;
  • }
  • else {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","delSymbols","(".join(", ",$symboles).")",$this->__toString().": Ce symbole en dehors de tous les cas.");
  • $bOK=0;
  • }
  • return $bOK;
  • }
  • /**
  • * @desc Accesseur
  • * @return array
  • * @param int $i
  • **/
  • public function getOriginalSymbols($i=null) {
  • if(isset($i) and $i>=0 and $i<count($this->m_aOSymbols)) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","getOriginalSymbols","($i)",$this->__toString().": Symbole #$i=".$this->m_aOSymbols[$i]);
  • return $this->m_aOSymbols[$i];
  • }
  • else {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","getOriginalSymbols","()",$this->__toString().": Symboles=".join(", ",$this->m_aOSymbols));
  • return $this->m_aOSymbols;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return array
  • * @param int $i
  • **/
  • public function getSymbols($i=null) {
  • if(isset($i) and $i>=0 and $i<count($this->m_aSymbols)) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","getSymbols","($i)",$this->__toString().": Symbole #$i=".$this->m_aSymbols[$i]);
  • return $this->m_aSymbols[$i];
  • }
  • else {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","getSymbols","()",$this->__toString().": Symboles=".join(", ",$this->m_aSymbols));
  • return $this->m_aSymbols;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return bool
  • * @param void
  • **/
  • public function isDone() {
  • if($this->m_bDone) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","isDone","()",$this->__toString().": true");
  • return true;
  • }
  • else {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","isDone","()",$this->__toString().": false");
  • return false;
  • }
  • }
  • /**
  • * @desc Accesseur
  • * @return bool
  • * @param void
  • **/
  • public function isEmpty() {
  • if(count($this->m_aSymbols)<=0) {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","isEmpty","()",$this->__toString().": true");
  • return true;
  • }
  • else {
  • //$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","isEmpty","()",$this->__toString().": false");
  • return false;
  • }
  • }
  • /**
  • * @desc Remet à zero.
  • * Si $shuffle est à vrai, génére une nouvelle série aléatoire (par défaut)
  • * Si $shuffle est à faux, conserve l'ordre des symboles généré précdemment.
  • * @return CSuDoKuCase
  • * @param bool $shuffle = true
  • **/
  • public function reSet($shuffle=true) {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","reSet","($shuffle)",$this->__toString().": réinitialisation");
  • $this->restaurer(-1);
  • unset($this->m_aSaveSymbols);
  • if($shuffle) shuffle($this->m_aSymbols);
  • $this->sauvegarder(-1);
  • return $this;
  • }
  • /**
  • * @desc Propage le fait que la case a été découverte
  • * @return bool
  • * @param
  • **/
  • public function propagate() {
  • if($this->m_oColumn->propagate($this->m_aSymbols[0],$this)){
  • ////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la colonne réussie");
  • if($this->m_oLine->propagate($this->m_aSymbols[0],$this)) {
  • ////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la ligne réussie");
  • if($this->m_oSquare->propagate($this->m_aSymbols[0],$this)) {
  • ////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la boîte réussie");
  • return true;
  • }
  • else {
  • ////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la boîte échouée");
  • //$this->m_oSquare->unpropagate($this->getAbsc(),$this->getOrd());
  • return false;
  • }
  • }
  • else {
  • ////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la ligne échouée");
  • //$this->m_oLine->unpropagate($this->getAbsc(),$this->getOrd());
  • return false;
  • }
  • }
  • else {
  • ////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la colonne échouée");
  • //$this->m_oColumn->unpropagate($this->getAbsc(),$this->getOrd());
  • return false;
  • }
  • }
  • /**
  • * @desc déPropage le fait que la case a été découverte
  • * @return CSudoKuCase
  • * @param
  • **/
  • public function unpropagate() {
  • //$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","unpropagate","()",$this->__toString().": dépropagation");
  • $this->m_oColumn->unpropagate($this);
  • $this->m_oLine->unpropagate($this);
  • $this->m_oSquare->unpropagate($this);
  • return $this;
  • }
  • /**
  • * @desc Renvoie le symbole temporaire du joueur
  • * @return string
  • * @param
  • **/
  • public function getTest() {
  • return $this->m_sTest;
  • }
  • /**
  • * @desc Permet de sauvegarder le choix temporaire du joueur
  • * @return CSudoKuCase
  • * @param string $sym
  • **/
  • public function test($sym) {
  • if(in_array($sym,$this->m_aOSymbols)){
  • $this->m_sTest=$sym;
  • return $this;
  • }
  • else return null;
  • }
  • /**
  • * @desc Permet d'effacer la sauvegarde du choix temporaire du joueur
  • * @return CSudoKuCase
  • * @param
  • **/
  • public function untest() {
  • $this->m_sTest=null;
  • return $this;
  • }
  • }
<?php
/**
 * @desc Generateur et solveur de grille de Sudoku *
 * @author Nifhell
 * @package sudoku
 * @version 1.0
 */
include_once($_SERVER['DOCUMENT_ROOT']."cobject.php");
/**
 * @desc Classe CSudoku
 *
 */
class CSuDoKu extends CObject {
	private $m_oDebug;
	private static $m_oInstance;
	protected $m_iNb;
	protected $m_sType;
	protected $m_aLines;
	protected $m_aCols;
	protected $m_aCases;
	protected $m_aSquare;
	protected $m_iLevel;
	protected $m_iCache;
	protected $m_bTutorial=false;
	protected $m_iTime;
	protected $m_iIter=-1;
	/**
	* @desc Constructeur par valeur
	* @return CSuDoKu
	* @param int $nb
	* @param string $type
	* @param int $level
	* @param bool $tuto
	**/
	public function CSuDoKu() {
		//$this->m_oDebug=CDebug::getInstance(false,true);
		$start=microtime(true);
		if(func_num_args()>1 and func_num_args()<=4) {
			func_get_arg(0)?$nb=func_get_arg(0):$nb=9;
			func_get_arg(1)?$type=func_get_arg(1):$type='numeric';
			func_get_arg(2)?$level=func_get_arg(2):$level=4;
			func_get_arg(3)?$tuto=func_get_arg(3):$tuto=false;
			$this->m_bTutorial=$tuto;
			$this->CSuDoKuGrille($nb,$type);
			$this->CSuDoKuLevel($level);
		}
		elseif (func_num_args()==1 and $param=func_get_arg(0)) {
			$this->XML2CSuDoKu($param);
		}
		$this->m_iTime=microtime(true)-$start;
		//$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","Constructor","($this->m_iNb,$this->m_sType,$this->m_iLevel,$this->m_bTutorial)","Sudoku initialisé: ".$this->m_iTime."s");
	}
	/**
	* @desc Détermine le nombre de cases a cacher
	* @return CSuDoKu
	* @param int $nb
	* @param string $type
	**/
	private function CSuDoKuGrille($nb,$type) {
		if(4<=$nb and $nb<=25
		and intval($sqrt=sqrt($nb))*10==$sqrt*10) {
			$this->m_iNb=$nb;
			$this->m_sType=$type;
			//$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","CSuDoKuGrille","($nb,$type)","Racine: $sqrt.");
			$this->m_aLines=array();
			$this->m_aCols=array();
			$this->m_aCases=array();
			$this->m_aSquare=array();
			for($i=0;$i<$nb;$i++) {
				if(!($this->m_aLines[$i] instanceof CSuDoKuLine)){
					$this->m_aLines[$i]=new CSuDoKuLine($i);
				}
				if(!($i%$sqrt)) {
					$this->m_aSquare[$i/$sqrt]=array();
				}
				$this->m_aCases[$i]=array();
				for($j=0;$j<$nb;$j++) {
					if(!($this->m_aCols[$j] instanceof CSuDoKuColumn)){
						$this->m_aCols[$j]=new CSuDoKuColumn($j);
					}
					$this->m_aCases[$i][$j]=new CSuDoKuCase($i,$j,$nb,$type);
					$this->m_aLines[$i]->addCase($this->m_aCases[$i][$j]);
					$this->m_aCols[$j]->addCase($this->m_aCases[$i][$j]);
					if(!($j%$sqrt) and !($i%$sqrt)) {
						$this->m_aSquare[$i/$sqrt][$j/$sqrt]=new CSuDoKuSquare($i/$sqrt,$j/$sqrt);
					}
					$this->m_aSquare[intval($i/$sqrt)][intval($j/$sqrt)]->addCase($this->m_aCases[$i][$j],$i-($sqrt*intval($i/$sqrt)),$j-($sqrt*intval($j/$sqrt)));
					$this->m_aCases[$i][$j]->square($this->m_aSquare[intval($i/$sqrt)][intval($j/$sqrt)]);
					$this->m_aCases[$i][$j]->line($this->m_aLines[$i]);
					$this->m_aCases[$i][$j]->column($this->m_aCols[$j]);
				}
			}
		}
		return $this;
	}
	/**
	* @desc Détermine le nombre de cases a cacher
	* @return CSuDoKu
	* @param int $level
	**/
	public function CSuDoKuLevel($level) {
		switch ($level){
			case 1:
				$this->m_iLevel=1;
				$base=mt_rand(30,35);
				break;
			case 2:
				$this->m_iLevel=2;
				$base=mt_rand(36,41);
				break;
			case 3:
				$this->m_iLevel=3;
				$base=mt_rand(42,47);
				break;
			case 4:
				$this->m_iLevel=4;
				$base=mt_rand(48,53);
				break;
			case 5:
				$this->m_iLevel=5;
				$base=mt_rand(54,59);
				break;
			case 6:
				$this->m_iLevel=6;
				$base=mt_rand(60,65);
				break;
			case 7:
				$this->m_iLevel=7;
				$base=mt_rand(66,71);
				break;
			default:
				$this->m_iLevel=4;
				$base=mt_rand(48,53);
		}
		$this->m_iCache=intval($base/100*pow($this->m_iNb,2));
		//$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","CSuDoKuLevel","($level)","Niveau: $this->m_iLevel, Caches: $this->m_iCache");
		return $this;
	}
	/**
	* @desc XML => CSuDoKu
	* @return CSuDoKu
	* @param string $xml_source
	**/
	private function XML2CSuDoKu($xml_source) {
		if(isset($xml_source)){
			if(!is_file($xml_source)){
				$xml=simplexml_load_string($xml_source);
				$this->CSuDoKu((int)$xml['nbr'],(string)$xml['type'],(int)$xml['level'],($xml['tuto']=='true'?true:false));
				foreach ($xml->cell as $cell) {
					$case=$this->m_aCases[(int)$cell['x']][(int)$cell['y']];
					if((string)$cell) $case->attribuer((string)$cell);
					if((string)$cell['test']) $case->test((string)$cell['test']);
				}
				////$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","XML2SuDoKu","($path)","XML => Chargement OK");
				return $this;
			}
			elseif(is_file($xml_source)) {
				$xml=simplexml_load_file($xml_source);
				$this->CSuDoKu((int)$xml['nbr'],(string)$xml['type'],(int)$xml['level'],($xml['tuto']=='true'?true:false));
				foreach ($xml->cell as $cell) {
					$case=$this->m_aCases[(int)$cell['x']][(int)$cell['y']];
					if((string)$cell) $case->attribuer((string)$cell);
					if((string)$cell['test']) $case->test((string)$cell['test']);
				}
				////$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","XML2SuDoKu","($path)","XML => Chargement OK");
				return $this;
			}
		}
		else {
			//$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","XML2SuDoKu","($path)","XML => Chargement ECHEC (Le fichier n'existe pas)");
			return null;
		}
	}
	/**
	* @desc Cache les cases de la grille de SuDoKu en cours
	* @return bool
	* @param void
	**/
	private function cache() {
		for($i=0;$i<$this->m_iCache;$i++) {
			do {
				$x=mt_rand(0,$this->m_iNb-1);
				$y=mt_rand(0,$this->m_iNb-1);
				$case=$this->m_aCases[$x][$y];
			}while(!$case->isDone());
			$case->reSet();
			//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","cache","($i)",$case->__toString()." cachée");
		}
		for($x=0;$x<$this->m_iNb;$x++) {
			for($y=0;$y<$this->m_iNb;$y++) {
				$case=$this->m_aCases[$x][$y];
				if($case->isDone()) {
					$case->attribuer($case->getSymbols(0));
				}
			}
		}
	}
	/**
	* @desc Résoud la grille de SuDoKu en cours
	* @return bool
	* @param int $i
	**/
	private function resoudre($i) {
		$tab="";
		$this->m_iIter++;
		//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$this->toStrTable());
		if(is_array($coord=$this->best2solve())) {
			$case=$this->m_aCases[$coord['x']][$coord['y']];
			//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab.$case->__toString()." choisie.");
			foreach($case->getSymbols() as $s) {
				//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."Symboles possibles de la ".$case->__toString().": ".join(", ",$case->getSymbols()));
				//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."\t Test du symbole $s: ");
				if($case->attribuer($s)) {
					//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."\t $s valider provisoirement.");
					if($this->resoudre($i+1)){
						//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."\t $s enterriner.");
						return true;
					}
					else {
						//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."\t $s ne convient pas au case d'ordre inférieur => on réinitialise la case symbole suivant.");
						$case->desattribuer();
					}
				}
				else {
					//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","resoudre","($i)",$tab."\t $s ne convient au case de la même ligne, colonne ou boite => symbole suivant.");
					$case->desattribuer();
				}
			}
			return false;
		}
		else return true;
	}
	/**
	* @desc Renvoie les coordonnées de la case qui a le moins de possibilité
	* @return array
	* @param
	**/
	public function best2solve() {
		$min=$this->m_iNb+1;
		$min_x=$min_y=-1;
		for($x=0;$x<$this->m_iNb;$x++) {
			for($y=0;$y<$this->m_iNb;$y++) {
				if(!$this->m_aCases[$x][$y]->isDone()){
					$tmp=count($this->m_aCases[$x][$y]->getSymbols());
					if($tmp<$min) {
						$min=$tmp;
						$min_x=$x;
						$min_y=$y;
					}
				}
			}
		}
		if($min>0 and $min!=$this->m_iNb+1) {
			//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKu","best2solve","()","Case best2solve: ".$this->m_aCases[$min_x][$min_y]->__toString()." minimum => $min.");
			return array('x' => $min_x, 'y' => $min_y, 0 => $min_x, 1 => $min_y);
		}
		else {
			//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKu","best2solve","()","Case best2solve: ".($this->m_aCases[$min_x][$min_y]?$this->m_aCases[$min_x][$min_y]->__toString():"")." | minimum => $min");
			return null;
		}
	}
	/**
	* @desc Génere une grille
	* @return CSuDoKu
	* @param
	**/
	public function genererGrille() {
		$start=microtime(true);
		$this->reSet();
		$bOK=$this->resoudre(0);
		$bOK=$bOK*$this->isValid();
		$this->cache();
		$bOK=$bOK*$this->isValid();
		$this->m_iTime=microtime(true)-$start;
		//$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","genererGrille","()","Grille initialisé (".$this->m_iTime."s): ".($bOK?"OK":"ECHEC"));
		return $bOK;
	}
	/**
	* @desc Résoud une grille non résolu
	* @return CSuDoKu
	* @param
	**/
	public function resoudreGrille() {
		$start=microtime(true);
		$bOK=$this->resoudre(0);
		$bOK=$bOK*$this->isValid();
		$this->m_iTime=microtime(true)-$start;
		//$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","resoudreGrille","()","Grille résolue (".$this->m_iTime."s): ".($bOK?"OK":"ECHEC"));
		return $bOK;
	}
	/**
	* @desc Renvoie le nombre d'itération que l'algo de résolution a effectuer
	* @return int
	* @param
	**/
	public function getIter() {
		return $this->m_iIter-pow($this->m_iNb,2);
	}
	/**
	* @desc Renvoie le nombre de case par ligne
	* @return int
	* @param
	**/
	public function getNbr() {
		return $this->m_iNb;
	}
	/**
	* @desc Renvoie le nombre de case par ligne
	* @return int
	* @param
	**/
	public function getLevel() {
		return $this->m_iLevel;
	}
	/**
	* @desc Résoud une grille non résolu
	* @return CSuDoKu
	* @param string $mode
	**/
	public function getTime($mode) {
		switch($mode){
			case 's':
				return round($this->m_iTime,2);
				break;
			case 'cs':
				return round($this->m_iTime*10,2);
				break;
			case 'ms':
				return round($this->m_iTime*100,2);
				break;
			case '_s':
				return round($this->m_iTime*1000,2);
				break;
			default:
				return round($this->m_iTime,2);
		}
	}
	/**
	* @desc Vide la grille.
	* 		Si $shuffle est à vrai, alors le prochain appel de résoudre(), générera une nouvelle grille (par défaut)
	* 		Si $shuffle est à faux, alors la grille est simplement vidé, mais le prochain appel de resoudre() générera la même grille que précédemment. Pratique, si vous souhiatez avoir la même grille mais avec des niveaux de difficulté différents.
	* @return CSuDoKu
	* @param bool $shuffle = true
	**/
	public function reSet($shuffle=true) {
		//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKu","reSet","()","initialisation");
		$this->m_iIter=-1;
		for($i=0;$i<count($this->m_aCases);$i++){
			for($j=0;$j<count($this->m_aCases[$i]);$j++){
				$this->m_aCases[$i][$j]->reSet($shuffle);
			}
		}
		return $this;
	}
	/**
	* @desc (Static) Renvoie l'instance en cours de la classe CSuDoKu
	* @return CSuDoKu
	* @param int $nb
	* @param string $type
	* @param int $level
	* @param bool $tuto
	**/
	public static function getInstance($nb,$type, $level, $tuto) {
		if (empty(self::$m_oInstance)) {
			self::$m_oInstance = new CSuDoKu($nb,$type, $level, $tuto);
		}
		return self::$m_oInstance;
	}
	/**
	* @desc CSuDoKu => SimpleXMLElement
	* @return SimpleXMLElement
	* @param string $path
	**/
	public function toXML($path=false) {
		$xml_string="<?xml version=\"1.0\" standalone=\"yes\"?>\n";
		$xml_string.="<sudoku type=\"".$this->m_sType."\" nbr=\"".$this->m_iNb."\" level=\"".$this->m_iLevel."\" tuto=\"".($this->m_bTutorial?'true':'false')."\">\n";
		for($i=0;$i<$this->m_iNb;$i++){
			for($j=0;$j<$this->m_iNb;$j++){
				if($this->getCase($i,$j)->getTest()) $test=$this->getCase($i,$j)->getTest();
				else $test="";
				$xml_string.="<cell x=\"".$i."\" y=\"".$j."\" test=\"".$test."\">";
				if($this->m_aCases[$i][$j]->isDone()) $xml_string.=$this->m_aCases[$i][$j]->getSymbols(0);
				$xml_string.="</cell>\n";
			}
		}
		$xml_string.="</sudoku>";
		//$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","toXML","($path)","Chargement de la grille: ".$xml_string);
		$xml=simplexml_load_string($xml_string);
		if((!isset($path) and $path!=false) or !$xml->asXML($path)) {
			//$this->m_oDebug->addTrace(1,$this->getObjectID(),"CSuDoKu","toXML","($path)","XML => Enregistrement du fichier XML ECHEC");
		}
		return $xml_string;
	}
	/**
	* @desc Transforme le SuDoKu en Table HTML
	* @return string
	* @param
	**/
	public function toHTMLTable() {
		$table="<table>\n\t";
		for($i=1;$i<=$this->m_iNb;$i++){
			$table.="<tr>";
			for($j=1;$j<=$this->m_iNb;$j++){
				$absc=$i-1;
				$ord=$j-1;
				$symbols=$this->getCase($absc,$ord)->getSymbols();
				$table.="<td>";
				if($this->getCase($absc,$ord)->isDone()){
					$table.=$symbols[0];
				}
				else {
					if($this->m_bTutorial) {
						$table.="<select name=\"case_".$i."_".$j."\" size=1>";
						$table.="<option value=\"null\">";
						natcasesort($symbols);
						foreach ($symbols as $sym) {
							$b="";
							if($this->getCase($absc,$ord)->getTest()) {
								if($sym==$this->getCase($absc,$ord)->getTest()) $b="selected";
							}
							$table.="<option value=\"".$sym."\" ".$b." >".$sym."";
						}
					}
					else {
						if($this->getCase($absc,$ord)->getTest()) $default=$this->getCase($absc,$ord)->getTest();
						else $default="";
						$table.="<input type=\"text\" name=\"case_".$i."_".$j."\" size=\"1\" maxlength=\"1\" onchange=\"check_sudoku_input(this)\" value=\"".$default."\">";
					}
				}
				$table.="</td>";
			}
			$table.="</tr>\n";
		}
		$table.="</table>\n";
		$table.="<script type=\"Javascript\">";
		$table.="function check_sudoku_input(nCase) {
			var Symbols=/^\s|".join("|",$this->getCase(0,0)->getOriginalSymbols())."$/;
			var bOk=false;
			if(nCase.value && !Symbols.test(nCase.value)) {
				alert('Arrrg! Vous devez saisir un caractère parmi ceux-ci: ".join(", ",$this->getCase(0,0)->getOriginalSymbols())."');
				nCase.value='';
				nCase.focus();
			}
			return bOk;
		}
		</script>\n";
		$this->setHTML($table);
		return $table;
	}
	/**
	* @desc Transforme le SuDoKu en Table
	* @return string
	* @param
	**/
	public function toStrTable() {
		$str="\n";
		for($i=0;$i<$this->m_iNb;$i++){
			if (!($i%sqrt($this->m_iNb))){
				for($j=1;$j<=$this->m_iNb;$j++){
					$str.="\t-";
				}
				$str.="\n";
			}
			for($j=0;$j<$this->m_iNb;$j++){
				if(!($j%sqrt($this->m_iNb))) {
					$str.="\t|";
				}
				$symbols=$this->getCase($i,$j)->getSymbols();
				if($this->getCase($i,$j)->isDone()){
					$str.="\t".$symbols[0];
				}
				else {
					$str.="\t ";
				}
			}
			$str.="\n";
		}
		return $str;
	}
	/**
	* @desc Transforme le SuDoKu en tableau HTML
	* @return string
	* @param void
	**/
	public function __toString() {
		return $this->toHTMLTable();
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuCase
	* @param int $i
	* @param int $j
	**/
	public function getCase($i,$j) {
		if($i<$this->m_iNb and $j<$this->m_iNb){
			return $this->m_aCases[$i][$j];
		}
		else {
			echo "<hr>Fatal error: CSudoku:getCase($i,$j):: out of range";
			return null;
		}
	}
	/**
	* @desc Vérifie si le mod tutoriel est activé
	* @return bool
	* @param
	**/
	public function isEasy() {
		return $this->m_bTutorial;
	}
	/**
	* @desc Vérifie les contraintes d'unicité
	* @return bool
	* @param
	**/
	public function isValid() {
		$bRet=array();
		for($x=0;$x<$this->m_iNb;$x++) {
			for($y=0;$y<$this->m_iNb;$y++) {
				if($this->m_aCases[$x][$y]->isEmpty()){
					$bRet[]=false;
					break;
				}
			}
		}
		foreach ($this->m_aLines as $obj) {
			$bRet[]=$obj->isValid();
		}
		foreach ($this->m_aCols as $obj) {
			$bRet[]=$obj->isValid();
		}
		foreach ($this->m_aSquare as $tmp) {
			foreach ($tmp as $obj) {
				$bRet[]=$obj->isValid();
			}
		}
		if(in_array(false,$bRet)) {
			//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","isValid","","Cette grille est fausse.");
			return false;
		}
		else {
			//$this->m_oDebug->addTrace(2,$this->getObjectID(),"CSuDoKu","isValid","","Cette grille est valide.");
			return true;
		}
	}
}
class CSuDoKuLine extends CObject {
	private $m_oDebug;
	protected $m_iOrd;
	protected $m_aCases;
	/**
	* @desc Constructeur par défaut
	* @return CSuDoKuLine
	* @param int $i
	**/
	public function CSuDoKuLine($i) {
		//$this->m_oDebug=CDebug::getInstance(false,true);
		if(intval($i)*10==$i*10){
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuLine","Constructor","($i)","$this");
			$this->m_iOrd=$i;
		}
	}
	/**
	* @desc Add a case
	* @return CSuDoKuLine
	* @param CSuDoKuCase $case
	**/
	public function addCase(CSuDoKuCase $case) {
		if($case instanceof CSuDoKuCase) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuLine","addCase","($case)","Ligne($this->m_iOrd)<=Case: (".$case->getAbsc().",".$case->getOrd().")");
			$this->m_aCases[]=$case;
			return $this;
		}
		else {
			echo "<hr>Fatal error: CSuDoKuLine::addCase(\$case) shouldn't be empty.";
			return null;
		}
	}
	/**
	* @desc Accesseur
	* @return array
	* @param
	**/
	public function getCases() {
		return $this->m_aCases;
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuCase
	* @param int $i
	**/
	public function getCase($i) {
		if($i>=0 and $i<count($this->m_aCases)) {
			return $this->m_aCases[$i];
		}
		else {
			echo "<hr>Fatal Error: \$i out of range.";
			return null;
		}
	}
	/**
	* @desc Accesseur
	* @return int
	* @param
	**/
	public function getOrd() {
		return $this->m_iOrd;
	}
	/**
	* @desc Vérifie les contraintes d'unicité
	* @return bool
	* @param
	**/
	public function isValid() {
		$bRet=true;
		$tmp=array();
		foreach ($this->m_aCases as $case) {
			if($case->isDone()) $tmp[]=$case;
		}
		foreach ($tmp as $case1) {
			$sym1=$case1->getSymbols();
			foreach ($tmp as $case2) {
				$sym2=$case2->getSymbols();
				if(($sym1[0]==$sym2[0] or !strcasecmp($sym1[0],$sym2[0])) and strcasecmp($case1->__toString(),$case2->__toString())) {
					//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSudoKuLine","isValid","","Violation de contrainte d'unicité de la ligne ".$this->m_iOrd.": ".$case1->__toString()."=".$case2->__toString()."=".$sym1[0]);
					$bRet=false;
				}
			}
		}
		return $bRet;
	}
	/**
	* @desc Propage le fait qu'une case a été découverte
	* @return CSuDoKuLine
	* @param mixed $symbole
	* @param CSuDoKuCase $cCase
	**/
	public function propagate($symbole,CSuDoKuCase $cCase) {
		$bOK=true;
		foreach ($this->m_aCases as $case) {
			if(!$case->isDone() and $case!==$cCase and $case->getSquare()!==$cCase->getSquare()){
				$case->delSymbols($symbole,true,$cCase->getObjectID());
				if($case->isEmpty()) {
					//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuLine","propagate","($symbole,$cCase)",$this->__toString().": ".$case->__toString()." est vide.");
					$bOK=false;
				}
			}
		}
		//$bOK?//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuLine","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation réussie.")://$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuLine","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation échouée.");
		return $bOK;
	}
	/**
	* @desc déPropage le fait qu'une case n'est plus découverte
	* @return CSuDoKuLine
	* @param CSuDoKuCase $cCase
	**/
	public function unpropagate(CSuDoKuCase $cCase) {
		////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuLine","unpropagate","(".$cCase->__toString().")",$this->__toString().": dépropagation");
		foreach ($this->m_aCases as $case) {
			if($case!==$cCase and $case->getSquare()!==$cCase->getSquare()){
				if(!$case->isDone()) {
					$case->restaurer($cCase->getObjectID());
				}
			}
		}
		return $this;
	}
	/**
	* @desc toString
	* @return string
	* @param
	**/
	public function __toString() {
		return "Ligne #".$this->m_iOrd;
	}
}
class CSuDoKuColumn extends CObject {
	private $m_oDebug;
	protected $m_iAbsc;
	protected $m_aCases;
	/**
	* @desc Constructeur par défaut
	* @return CSuDoKuColumn
	* @param int $i
	**/
	public function CSuDoKuColumn($i) {
		//$this->m_oDebug=CDebug::getInstance(false,true);
		if(intval($i)*10==$i*10){
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuColumn","Constructor","($i)","$this");
			$this->m_iAbsc=$i;
		}
	}
	/**
	* @desc Add a case
	* @return CSuDoKuColumn
	* @param CSuDoKuCase $case
	**/
	public function addCase(CSuDoKuCase $case) {
		if($case instanceof CSuDoKuCase) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuColumn","addCase","($case)","Colonne($this->m_iAbsc)<=Case: (".$case->getAbsc().",".$case->getOrd().")");
			$this->m_aCases[]=$case;
			return $this;
		}
		else {
			echo "<hr>Fatal error: CSuDoKuColumn::addCase(\$case) shouldn't be empty.";
			return null;
		}
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuCase
	* @param int $i
	**/
	public function getCase($i) {
		if($i>=0 and $i<count($this->m_aCases)) {
			return $this->m_aCases[$i];
		}
		else {
			echo "<hr>Fatal Error: \$i out of range.";
			return null;
		}
	}
	/**
	* @desc Accesseur
	* @return array
	* @param
	**/
	public function getCases() {
		return $this->m_aCases;
	}
	/**
	* @desc Accesseur
	* @return int
	* @param void
	**/
	public function getAbsc() {
		return $this->m_iAbsc;
	}
	/**
	* @desc Accesseur
	* @return bool
	* @param void
	**/
	public function isValid() {
		$bRet=true;
		$tmp=array();
		foreach ($this->m_aCases as $case) {
			if($case->isDone()) $tmp[]=$case;
		}
		foreach ($tmp as $case1) {
			$sym1=$case1->getSymbols();
			foreach ($tmp as $case2) {
				$sym2=$case2->getSymbols();
				if(($sym1[0]==$sym2[0] or !strcasecmp($sym1[0],$sym2[0])) and strcasecmp($case1->__toString(),$case2->__toString())) {
					//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuColumn","isValid","","Violation de contrainte d'unicité de la Colonne ".$this->m_iAbsc.": ".$case1->__toString()."=".$case2->__toString()."=".$sym1[0]);
					$bRet=false;
				}
			}
		}
		return $bRet;
	}
	/**
	* @desc Propage le fait qu'une case a été découverte
	* @return CSuDoKuColumn
	* @param mixed $symbole
	* @param CSuDoKuCase $cCase
	**/
	public function propagate($symbole,CSuDoKuCase $cCase) {
		$bOK=true;
		foreach ($this->m_aCases as $case) {
			if(!$case->isDone() and $case!==$cCase and $case->getSquare()!==$cCase->getSquare()) {
				$case->delSymbols($symbole,true,$cCase->getObjectID());
				if($case->isEmpty()) {
					//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuColumn","propagate","($symbole,$absc,$ord)",$this->__toString().": ".$case->__toString()." est vide");
					$bOK=false;
				}
			}
		}
		//$bOK?//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuColumn","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation réussie.($bOK)")://$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuColumn","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation échouée.");
		return $bOK;
	}
	/**
	* @desc déPropage le fait qu'une case a été remis à l'état incertain
	* @return CSuDoKuColumn
	* @param CSuDoKuCase $cCase
	**/
	public function unpropagate(CSuDoKuCase $cCase) {
		////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuColumn","unpropagate","(".$cCase->__toString().")",$this->__toString().": dépropagation");
		foreach ($this->m_aCases as $case) {
			if($case!==$cCase and $case->getSquare()!==$cCase->getSquare()){
				if(!$case->isDone()) {
					$case->restaurer($cCase->getObjectID());
				}
			}
		}
		return $this;
	}
	/**
	* @desc toString
	* @return string
	* @param void
	**/
	public function __toString() {
		return "Colonne #".$this->m_iAbsc;
	}
}
class CSuDoKuSquare extends CObject {
	private $m_oDebug;
	protected $m_iAbsc;
	protected $m_iOrd;
	protected $m_aCases;
	/**
	* @desc Constructeur par défaut
	* @return CSuDoKuSquare
	* @param int $i
	* @param int $j
	**/
	public function CSuDoKuSquare($i,$j) {
		//$this->m_oDebug=CDebug::getInstance(false,true);
		if(intval($i)*10==$i*10 and intval($j)*10==$j*10){
			$this->m_iAbsc=$i;
			$this->m_iOrd=$j;
			$this->m_aCases=array();
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuSquare","Constructor","($i,$j)","$this");
		}
		else echo "<hr>Fatal error: CSuDoKuSquare(\$i,\$j,\$sym) shouldn't be empty.";
	}
	/**
	* @desc Add a case
	* @return CSuDoKuSquare
	* @param CSuDoKuCase $case
	* @param int $i
	* @param int $j
	**/
	public function addCase(CSuDoKuCase $case,$i,$j) {
		if($case instanceof CSuDoKuCase) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuSquare","addCase","($case,$i,$j)","Carré(".$this->m_iAbsc.",".$this->m_iOrd.")<=Case: (".$case->getAbsc().",".$case->getOrd().")");
			if(!is_array($this->m_aCases[$i])) $this->m_aCases[$i]=array();
			$this->m_aCases[$i][$j]=$case;
			return $this;
		}
		else {
			echo "<hr>Fatal error: CSuDoKuSquare::addCase(\$case) shouldn't be empty.";
			return null;
		}
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuCase
	* @param int $i
	* @param int $j
	**/
	public function getCase($i,$j) {
		if($i>=0 and $i<count($this->m_aCases) and $j>=0 and $j<count($this->m_aCases[$i])) {
			return $this->m_aCases[$i][$j];
		}
		else {
			echo "<hr>Fatal Error: \$i or \$j out of range.";
			return null;
		}
	}
	/**
	* @desc Accesseur
	* @return array
	* @param void
	**/
	public function getCases() {
		return $this->m_aCases;
	}
	/**
	* @desc Accesseur
	* @return int
	* @param void
	**/
	public function getAbsc() {
		return $this->m_iAbsc;
	}
	/**
	* @desc Accesseur
	* @return int
	* @param void
	**/
	public function getOrd() {
		return $this->m_iOrd;
	}
	/**
	* @desc Accesseur
	* @return string
	* @param void
	**/
	public function __toString() {
		return "Carre[".$this->getObjectID()."](".$this->m_iAbsc.",".$this->m_iOrd.")";
	}
	/**
	* @desc Accesseur
	* @return bool
	* @param void
	**/
	public function isValid() {
		$bRet=true;
		$tmp=array();
		foreach ($this->m_aCases as $col) {
			foreach ($col as $case) {
				if($case->isDone()) $tmp[]=$case;
			}
		}
		foreach ($tmp as $col1) {
			foreach ($col1 as $case1) {
				$sym1=$case1->getSymbols();
				foreach ($tmp as $col2) {
					foreach ($col2 as $case2) {
						$sym2=$case2->getSymbols();
						if(($sym1[0]==$sym2[0] or !strcasecmp($sym1[0],$sym2[0])) and strcasecmp($case1->__toString(),$case2->__toString())) {
							//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuSquare","isValid","","Violation de contrainte d'unicité du Carré ".$this->__toString().": ".$case1->__toString()."=".$case2->__toString()."=".$sym1[0]);
							$bRet=false;
						}
					}
				}
			}
		}
		return $bRet;
	}
	/**
	* @desc Propage le fait qu'une case a été découverte
	* @return CSuDoKuSquare
	* @param mixed $symbole
	* @param CSuDoKuCase $cCase
	**/
	public function propagate($symbole,CSuDoKuCase $cCase) {
		$bOK=true;
		foreach ($this->m_aCases as $cases) {
			foreach ($cases as $case) {
				if(!$case->isDone() and $case!==$cCase){
					$case->delSymbols($symbole,true,$cCase->getObjectID());
					if($case->isEmpty()) {
						//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuSquare","propagate","($symbole,$cCase)",$this->__toString().": ".$case->__toString()." est vide.");
						$bOK=false;
					}
				}
			}
		}
		//$bOK?//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuSquare","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation réussie.")://$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuSquare","propagate","($symbole,$absc,$ord)",$this->__toString().": propagation échouée.");
		return $bOK;
	}
	/**
	* @desc déPropage le fait qu'une case a été découverte
	* @return CSuDoKuSquare
	* @param CSuDoKuCase $cCase
	**/
	public function unpropagate(CSuDoKuCase $cCase) {
		////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuSquare","unpropagate","(".$cCase->__toString().")",$this->__toString().": dépropagation");
		foreach ($this->m_aCases as $cases) {
			foreach ($cases as $case) {
				if($case!==$cCase){
					if(!$case->isDone()) {!
					$case->restaurer($cCase->getObjectID());
					}
				}
			}
		}
		return $this;
	}
}
/**
 * @desc Classe CSuDoKuCase
 *
 */
class CSuDoKuCase extends CObject {
	protected $m_oDebug;
	protected $m_iAbsc;
	protected $m_iOrd;
	protected $m_aSaveSymbols;
	protected $m_aSymbols;
	protected $m_aOSymbols;
	protected $m_oSquare;
	protected $m_oLine;
	protected $m_oColumn;
	protected $m_iNb;
	protected $m_sType;
	protected $m_bDone;
	protected $m_sTest=null;
	/**
	* @desc Constructeur par défaut
	* @return CSuDoKuCase
	* @param int $i
	* @param int $j
	* @param int $nb
	* @param string $type
	**/
	public function CSuDoKuCase($i,$j,$nb,$type) {
		//$this->m_oDebug=CDebug::getInstance(false,true);
		if(intval($i*10)==$i*10 and intval($j*10)==$j*10 ) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","Constructor","($i,$j,$nb,$type)","$this");
			$this->m_iAbsc=$i;
			$this->m_iOrd=$j;
			$this->m_iNb=$nb;
			$this->m_bDone=false;
			$this->m_aSaveSymbols=array();
			switch($type) {
				case 'numeric':
					$this->m_sType='numeric';
					$this->m_aSymbols=array();
					for($i=1;$i<=$nb;$i++) {
						$this->m_aSymbols[]=$i;
						$this->m_aOSymbols[]=$i;
					}
					shuffle($this->m_aSymbols);
					$this->sauvegarder(-1);
					break;
				case 'alpha':
					$this->m_sType='alpha';
					$this->m_aSymbols=array();
					for($i=0;$i<$nb;$i++) {
						$this->m_aSymbols[]=chr(65+$i);
						$this->m_aOSymbols[]=chr(65+$i);
					}
					shuffle($this->m_aSymbols);
					$this->sauvegarder(-1);
					break;
				default: echo "<hr>Fatal error: CSuDoKuCase(\$type) should be 'numeric' or 'alpha'.";
			}
		}
		else echo "<hr>Fatal error: CSuDoKuCase(\$i, \$j, \$nb,\$type) shouldn't be empty or \$i and \$j should be integers and \$sym should be a CSuDoKuSymboles";
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuCase
	* @param CSuDoKuSquare $square
	**/
	public function square(CSuDoKuSquare $square) {
		if($square instanceof CSuDoKuSquare ) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","square","($square)","Association de la boîte (".$square->getAbsc().",".$square->getOrd().") à ".$this->__toString());
			$this->m_oSquare=$square;
			return $this;
		}
		else {
			echo "<hr>\nCSuDoKuCase::square(\$square)::ERROR:: \$square should be an CSuDoKuSquare object";
			return null;
		}
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuCase
	* @param CSuDoKuLine $line
	**/
	public function line(CSuDoKuLine $line) {
		if($line instanceof CSuDoKuLine) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","line","($line)","Association de la ligne(".$line->getOrd().") à ".$this->__toString());
			$this->m_oLine=$line;
			return $this;
		}
		else {
			echo "<hr>\nCSuDoKuCase::line(\$line)::ERROR:: \$line should be an CSuDoKuLine object";
			return null;
		}
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuCase
	* @param CSuDoKuColumn $column
	**/
	public function column(CSuDoKuColumn $column) {
		if($column instanceof CSuDoKuColumn) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","column","($column)","Association de la colonne (".$column->getAbsc().") à ".$this->__toString());
			$this->m_oColumn=$column;
			return $this;
		}
		else {
			echo "<hr>\nCSuDoKuCase::column(\$column)::ERROR:: \$column should be an CSuDoKuColumn object";
			return null;
		}
	}
	/**
	* @desc Accesseur
	* @return int
	* @param void
	**/
	public function getRelativeOrd() {
		return $this->m_iOrd-sqrt($this->m_iNb)*$this->m_oSquare->getOrd();
	}
	/**
	* @desc Accesseur
	* @return int
	* @param void
	**/
	public function getRelativeAbsc() {
		return $this->m_iAbsc-sqrt($this->m_iNb)*$this->m_oSquare->getAbsc();
	}
	/**
	* @desc Accesseur
	* @return int
	* @param void
	**/
	public function getOrd() {
		return $this->m_iOrd;
	}
	/**
	* @desc Accesseur
	* @return int
	* @param void
	**/
	public function getAbsc() {
		return $this->m_iAbsc;
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuLine
	* @param void
	**/
	public function getLine() {
		return $this->m_oLine;
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuColumn
	* @param void
	**/
	public function getColumn() {
		return $this->m_oColumn;
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuSquare
	* @param void
	**/
	public function getSquare() {
		return $this->m_oSquare;
	}
	/**
	* @desc Renvoie string
	* @return string
	* @param void
	**/
	public function __toString() {
		return "Case[".$this->getObjectID()."](".$this->m_iAbsc.",".$this->m_iOrd.")";
	}
	/**
	* @desc Sauvegarde les symboles qui ont été modifié par la case dont l'id est $idCase
	* @return bool
	* @param int $idCase
	**/
	public function sauvegarder($idCase) {
		if(!$this->isDone() and !$this->isEmpty()){
			//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","sauvegarder","($idCase)",$this->__toString()."=> sauvegarde n°".$idCase.": (".join(", ",$this->m_aSymbols).")");
			$this->m_aSaveSymbols[$idCase]=$this->m_aSymbols;
		}
		else {
			if($this->isDone()) {
				//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","sauvegarder","($idCase)",$this->__toString()."=> non sauvegarde car découverte");
			}
			else {
				if($this->isEmpty()) {
					//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","sauvegarder","($idCase)",$this->__toString()."=> non sauvegarde car vide");
				}
			}
		}
	}
	/**
	* @desc Restaure les symboles
	* @return bool
	* @param int $idCase
	**/
	public function restaurer($idCase) {
		if(count($this->m_aSaveSymbols)>0) {
			if(is_array($this->m_aSaveSymbols[$idCase])) {
				//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","restaurer","($idCase)",$this->__toString()."=> restauration de la sauvegarde n°".$idCase.": (".join(", ",$this->m_aSaveSymbols[$idCase]).")");
				$this->m_aSymbols=$this->m_aSaveSymbols[$idCase];
				if(count($this->m_aSymbols)>1) $this->m_bDone=false;
				if(count($this->m_aSaveSymbols)>1) unset($this->m_aSaveSymbols[$idCase]);
			}
			else {
				//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","restaurer","($idCase)",$this->__toString()."=> La sauvegarde n°".$idCase." n'existe pas.");
			}
		}
		else {
			//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","restaurer","($idCase)",$this->__toString()."=> pas assez de sauvegarde disponible: ".count($this->m_aSaveSymbols));
		}
	}
	/**
	* @desc Attribue un symbole
	* @return bool
	* @param string $sym
	**/
	public function attribuer($sym){
		if($this->setSymbols($sym,$this->getObjectID())) {
			$this->m_bDone=true;
			//$this->m_oDebug->addTrace(3,$this->getObjectID(),"CSuDoKuCase","attribuer","(".$sym.")",$this->__toString()."=> attribue $sym");
			return $this->propagate();
		}
		else {
			//$this->m_oDebug->addTrace(3,$this->getObjectID(),"CSuDoKuCase","attribuer","(".$sym.")",$this->__toString()."=> setSymbols($sym) a échoué");
			$this->m_bDone=false;
			return false;
		}
	}
	/**
	* @desc DesAttribue un symbole
	* @return CSuDoKuCase
	**/
	public function desattribuer() {
		//$this->m_oDebug->addTrace(3,$this->getObjectID(),"CSuDoKuCase","desattribuer","()",$this->__toString()."=> désattribue ");
		$this->m_bDone=false;
		$this->restaurer($this->getObjectID());
		return $this->unpropagate();
	}
	/**
	* @desc Accesseur
	* @return CSuDoKuCase
	* @param mixed $symboles
	* @package int $idCase
	**/
	public function setSymbols($symboles,$idCase) {
		$this->sauvegarder($idCase);
		$bOK=1;
		if(is_array($symboles)){
			$symboles=array_unique($symboles);
			if(count($this->m_aOSymbols)>=count($symboles) and count($symboles)>0) {
				//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","setSymbols","(".$symboles.")",$this->__toString().": Modification de la liste des symboles.");
				$a_enlever=array_diff($this->m_aSymbols,$symboles);
				$a_ajouter=array_diff($symboles,$this->m_aSymbols);
				if(count($a_enlever)) {
					foreach ($a_enlever as $sym) {
						$bOK=$bOK*$this->delSymbols($sym,false);
					}
				}
				if(count($a_ajouter)) {
					foreach ($a_ajouter as $sym) {
						$bOK=$bOK*$this->addSymbols($sym,false);
					}
				}
			}
			else $bOK=0;
		}
		elseif(!is_array($symboles) and in_array($symboles,$this->m_aOSymbols)) {
			////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","setSymbols","(".$symboles.")",$this->__toString().": isDone() => true, Symbole=$symboles.");
			$this->m_aSymbols=array(0 => $symboles);
			$bOK=1;
		}
		else {
			//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","setSymbols","(".join(", ",$symboles).")",$this->__toString().": Le tableau est vide ou contient trop de symboles, ou ce symbole n'existe pas.");
			$bOK=0;
		}
		return $bOK;
	}
	/**
	* @desc Accesseur
	* @return CSuDoKu
	* @param mixed $symboles
	* @param bool $bSave
	* @param int $idCase
	**/
	public function addSymbols($symboles,$bSave,$idCase) {
		if($bSave) $this->sauvegarder($idCase);
		$bOK=1;
		if(is_array($symboles)){
			$symboles=array_unique($symboles);
			if(count($symboles)>0) {
				$a_ajouter=array_diff($symboles,$this->m_aSymbols);
				if(count($a_ajouter)>0) {
					foreach ($a_ajouter as $sym) {
						$bOK=$bOK*$this->addSymbols($sym,false);
					}
				}
			}
			else $bOK=0;
		}
		elseif(!is_array($symboles) and in_array($symboles,$this->m_aSymbols)) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","addSymbols","(".$symboles.")",$this->__toString().": Ce symbole existe déja.");
			$bOK=0;
		}
		elseif(!is_array($symboles) and !in_array($symboles,$this->m_aSymbols) and in_array($symboles,$this->m_aOSymbols)) {
			$this->m_aSymbols[]=$symboles;
			//shuffle($this->m_aSymbols);
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","addSymbols","(".$symboles.")",$this->__toString().": Ajout de $symboles:".join(", ",$this->m_aSymbols));
			$bOK=1;
		}
		else {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","addSymbols","(".$symboles.")",$this->__toString().": Ce symbole en dehors de tous les cas.");
			$bOK=0;
		}
		return $bOK;
	}
	/**
	* @desc Accesseur
	* @return CSuDoKu
	* @param mixed $symboles
	* @param bool $bSave
	* @param int $idCase
	**/
	public function delSymbols($symboles,$bSave,$idCase) {
		if($bSave) $this->sauvegarder($idCase);
		$bOK=1;
		if(is_array($symboles)) {
			$symboles=array_unique($symboles);
			if(count($symboles)>0) {
				$a_enlever=array_diff($this->m_aSymbols,$symboles);
				if(count($a_enlever)>0) {
					foreach ($a_enlever as $sym) {
						$bOK=$bOK*$this->delSymbols($sym,false);
					}
				}
			}
			else {
				$bOK=0;
			}
		}
		elseif(!is_array($symboles) and !in_array($symboles,$this->m_aSymbols)) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","delSymbols","(".$symboles.")",$this->__toString().": Ce symbole n'existe déja plus.");
			$bOK=0;
		}
		elseif(!is_array($symboles) and in_array($symboles,$this->m_aSymbols)) {
			unset($this->m_aSymbols[array_search($symboles,$this->m_aSymbols)]);
			//shuffle($this->m_aSymbols);
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","delSymbols","(".$symboles.")",$this->__toString().": Suppression de $symboles:".join(", ",$this->m_aSymbols));
			$bOK=1;
		}
		else {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","delSymbols","(".join(", ",$symboles).")",$this->__toString().": Ce symbole en dehors de tous les cas.");
			$bOK=0;
		}
		return $bOK;
	}
	/**
	* @desc Accesseur
	* @return array
	* @param int $i
	**/
	public function getOriginalSymbols($i=null) {
		if(isset($i) and $i>=0 and $i<count($this->m_aOSymbols)) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","getOriginalSymbols","($i)",$this->__toString().": Symbole #$i=".$this->m_aOSymbols[$i]);
			return $this->m_aOSymbols[$i];
		}
		else {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","getOriginalSymbols","()",$this->__toString().": Symboles=".join(", ",$this->m_aOSymbols));
			return $this->m_aOSymbols;
		}
	}
	/**
	* @desc Accesseur
	* @return array
	* @param int $i
	**/
	public function getSymbols($i=null) {
		if(isset($i) and $i>=0 and $i<count($this->m_aSymbols)) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","getSymbols","($i)",$this->__toString().": Symbole #$i=".$this->m_aSymbols[$i]);
			return $this->m_aSymbols[$i];
		}
		else {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","getSymbols","()",$this->__toString().": Symboles=".join(", ",$this->m_aSymbols));
			return $this->m_aSymbols;
		}
	}
	/**
	* @desc Accesseur
	* @return bool
	* @param void
	**/
	public function isDone() {
		if($this->m_bDone) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","isDone","()",$this->__toString().": true");
			return true;
		}
		else {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","isDone","()",$this->__toString().": false");
			return false;
		}
	}
	/**
	* @desc Accesseur
	* @return bool
	* @param void
	**/
	public function isEmpty() {
		if(count($this->m_aSymbols)<=0) {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","isEmpty","()",$this->__toString().": true");
			return true;
		}
		else {
			//$this->m_oDebug->addTrace(7,$this->getObjectID(),"CSuDoKuCase","isEmpty","()",$this->__toString().": false");
			return false;
		}
	}
	/**
	* @desc Remet à zero.
	* 		Si $shuffle est à vrai, génére une nouvelle série aléatoire (par défaut)
	* 		Si $shuffle est à faux, conserve l'ordre des symboles généré précdemment.
	* @return CSuDoKuCase
	* @param bool $shuffle = true
	**/
	public function reSet($shuffle=true) {
		//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","reSet","($shuffle)",$this->__toString().": réinitialisation");
		$this->restaurer(-1);
		unset($this->m_aSaveSymbols);
		if($shuffle) shuffle($this->m_aSymbols);
		$this->sauvegarder(-1);
		return $this;
	}
	/**
	* @desc Propage le fait que la case a été découverte
	* @return bool
	* @param
	**/
	public function propagate() {
		if($this->m_oColumn->propagate($this->m_aSymbols[0],$this)){
			////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la colonne réussie");
			if($this->m_oLine->propagate($this->m_aSymbols[0],$this)) {
				////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la ligne réussie");
				if($this->m_oSquare->propagate($this->m_aSymbols[0],$this)) {
					////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la boîte réussie");
					return true;
				}
				else {
					////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la boîte échouée");
					//$this->m_oSquare->unpropagate($this->getAbsc(),$this->getOrd());
					return false;
				}
			}
			else {
				////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la ligne échouée");
				//$this->m_oLine->unpropagate($this->getAbsc(),$this->getOrd());
				return false;
			}
		}
		else {
			////$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","propagate","()",$this->__toString().": propagation de la colonne échouée");
			//$this->m_oColumn->unpropagate($this->getAbsc(),$this->getOrd());
			return false;
		}
	}
	/**
	* @desc déPropage le fait que la case a été découverte
	* @return CSudoKuCase
	* @param
	**/
	public function unpropagate() {
		//$this->m_oDebug->addTrace(4,$this->getObjectID(),"CSuDoKuCase","unpropagate","()",$this->__toString().": dépropagation");
		$this->m_oColumn->unpropagate($this);
		$this->m_oLine->unpropagate($this);
		$this->m_oSquare->unpropagate($this);
		return $this;
	}
	/**
	* @desc Renvoie le symbole temporaire du joueur
	* @return string
	* @param
	**/
	public function getTest() {
		return $this->m_sTest;
	}
	/**
	* @desc Permet de sauvegarder le choix temporaire du joueur
	* @return CSudoKuCase
	* @param string $sym
	**/
	public function test($sym) {
		if(in_array($sym,$this->m_aOSymbols)){
			$this->m_sTest=$sym;
			return $this;
		}
		else return null;
	}
	/**
	* @desc Permet d'effacer la sauvegarde du choix temporaire du joueur
	* @return CSudoKuCase
	* @param
	**/
	public function untest() {
		$this->m_sTest=null;
		return $this;
	}
}

Conclusion

Si vous souhaitez le voir en fonctionnement rendez vous sur http://nifhell.free.fr.
Le seul bug connu à ce jour, est que les Sudoku générés ne sont pas forcément à solution unique.
 

Fichier Zip

Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

Historique

19 avril 2006 22:30:54 :
Rajout de la liste des fonctionnalites disponibles.
20 avril 2006 19:33:17 :
Rajout du ZIP
20 avril 2006 19:38:06 :
Mise à jour du ZIP ;p
23 avril 2006 10:30:13 :
Correction Titre + Catégorie
29 août 2006 13:24:22 :
Correction de la fonction magique "__toString()" qui faisait référence à une fonction inconnue

Commentaires et avis

signaler à un administrateur
Commentaire de malalam le 20/04/2006 07:48:24 administrateur CS

Hello,

je serais toi, je mettrais aussi un zip...c'est pas que, mais c'est lon à copier ton truc :-)
Bon sinon il est un peu tôt, là, mais...ça a l'air très joliment codé !
Je mate ça de plus près un peu plus tard.

signaler à un administrateur
Commentaire de Anthomicro le 20/04/2006 08:30:44

Salut :-)

10, c'est vraiment pas mal du tout (je suis pas un matheux donc bon...) ;-)

Pas grand chose à redire niveau PHP, peut-être niveau XHTML l'attribut name du formulaire qui est déprécié...

Bref 10/10 pour ma part.

Bonne continuation :-)

Antho

signaler à un administrateur
Commentaire de galadriann le 20/04/2006 08:47:08

Ca a l'air interessant et bien fait mais j'obtiens un erreur lors de l'execution :

Fatal error: Call to a member function setValue() on a non-object in B:\www\Sudoku\sudoku.php on line 5

line 5 : $session->setValue('sudoku',serialize($sudoku),false);

il ne manquerait pas la declaration de $session ?

signaler à un administrateur
Commentaire de Nifhell le 20/04/2006 09:33:17

Oui, effectivement, il s'agit juste d'un exemple incomplet d'utilisation de l'objet sudoku avec les sessions: il manque toute la gestion des sessions
D'ailleurs dans l'exemple, j'utilise un objet $session qui n'est pas instancié, mais qui chez moi est une instance de classe "CSession" qui gére l'ouverture, fermeture et la sécurisation des sessions des utilisateurs authentifiés sur mon site.

Bref je fais une mise à jour ce soir, parce que là je suis au taf ;p

signaler à un administrateur
Commentaire de galadriann le 20/04/2006 10:26:01

ok. .. j'attend ca avec impatience ...

Merci

signaler à un administrateur
Commentaire de johann1 le 20/04/2006 15:29:18

Ca à l'air chouette Nifhell, je veux me tenir informé de la suite.
Merci et bonne continuation

signaler à un administrateur
Commentaire de malalam le 21/04/2006 10:09:40 administrateur CS

Faiut que je le teste, mais décidément, en approfondissant le code,
je trouve ça très joli :-) 10.

signaler à un administrateur
Commentaire de Mawashigeri le 21/04/2006 18:36:14

Oui joli, joli,

Dis moi, est ce que c'est le code officiel que tu as retravaillé ?
Ce que je veux dire c'est que l'algorithme est il celui du sudoku officiel -> donc, pas de possibilité de "faux-sudoku"?

signaler à un administrateur
Commentaire de Nifhell le 23/04/2006 10:41:19

Non non désolé Mawashigeri,

Ce code peut générer des Sudoku qui ont plus d'une solution, ce qui n'est pas un "sudoku officiel"

signaler à un administrateur
Commentaire de wizard512 le 23/04/2006 11:26:49

Trés joli source, tu ne la pas coder pour rien,
bref je pense que tu mérite largement ton 10/10 !!!
et Encore bravo pour cette joli source et bonne continuation  ;)

signaler à un administrateur
Commentaire de Pyrghos le 24/04/2006 09:19:26

Salut, l'idée est exelente, je me suis rendu sur ton site afin de le tester ce qui a fini de me convaincre ;)

Tu ne vera pas mon nom dans tes stats, je ne suis malheureusement pas du niveau souhaitez :/

Un souci cependant, j'ai télécharger ton fichier ZIP afin de le tester en local, mais impossible de le faire fonctionner. Voici les message d'erreur qui me sont renvoyer :

--------------------------------------\

Warning: main(d:/sitescsudoku.php) [function.main]: failed to open stream: No such file or directory in d:\Sites\Sudoku\exemples.php on line 4

Warning: main() [function.include]: Failed opening 'd:/sitescsudoku.php' for inclusion (include_path='.;C:\php5\pear') in d:\Sites\Sudoku\exemples.php on line 4

Warning: session_start() [function.session-start]: Cannot send session cookie - headers already sent by (output started at d:\Sites\Sudoku\exemples.php:2) in d:\Sites\Sudoku\exemples.php on line 5

Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at d:\Sites\Sudoku\exemples.php:2) in d:\Sites\Sudoku\exemples.php on line 5

Fatal error: Class 'CSuDoKu' not found in d:\Sites\Sudoku\exemples.php on line 6

--------------------------------------/

Si tu peut m'expliquer comment m'y prendre pour que cela fonctionne chez moi je t'en serait trés reconnaissant.

^^

signaler à un administrateur
Commentaire de coucou747 le 24/04/2006 09:32:07

"- Les grilles ne sont pas forcément à solution unique "=>
pour ma part, je les importe à partir de websudoku (j'ai trois sources qui font ça, une qui est comme la tienne, mais en gtk, l'autre qui ne fait que la résolution, et une dernière qui ne fait que l'importation pour imprimer)

Je crois que la seule solution pour faire une grille à solutions uniques, c'est de résoudre la grille, et de voir si il reste plusieurs solutions dans une case, alors on y place un chiffre.

pour ta résolution, tu ne fais pas une résolution par déduction, tu la fais par supposition, c'est domage, personellement, j'ai choisi un mélange des deux, et j'ai des performances satistaisantes...

signaler à un administrateur
Commentaire de Nifhell le 24/04/2006 15:27:22

Pyrghos,

Warning: main(d:/sitescsudoku.php) [function.main]: failed to open stream: No such file or directory in d:\Sites\Sudoku\exemples.php on line 4

Warning: main() [function.include]: Failed opening 'd:/sitescsudoku.php' for inclusion (include_path='.;C:\php5\pear') in d:\Sites\Sudoku\exemples.php on line 4

Ces 2 erreurs sont dues au fait que le chemin passé en paramètre à la fonction include_once du fichier exemple.php est faux, il manque un "/" ou "\" => "d:\sites\csudoku.php" à la place de "d:\sitescsudoku.php". Du coup il trouve pas le fichier "csudoku.php" et te sort l'erreur:
Fatal error: Class 'CSuDoKu' not found in d:\Sites\Sudoku\exemples.php on line 6

La 3éme alerte est du au fait que les erreurs précédentes ont déja fait envoyer des données par le serveur au client, du coup les fonctions de session ou de cookies ne peuvent plus être effectives.

Warning: session_start() [function.session-start]: Cannot send session cookie - headers already sent by (output started at d:\Sites\Sudoku\exemples.php:2) in d:\Sites\Sudoku\exemples.php on line 5

Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at d:\Sites\Sudoku\exemples.php:2) in d:\Sites\Sudoku\exemples.php on line 5


Le fichier exemple est la à titre d'exemple, il faut adapater un peu pour le faire tourner sur ton serveur... :)

signaler à un administrateur
Commentaire de Pyrghos le 24/04/2006 16:15:19

Merci Nifhell ;)

Au fait j'ai bien aimer le graphisme de ton site, sa sent le coup de coeur de l'artiste ^^

Je code aussi mais je n'ai pas ta patience, et j'ai pourtant bien l'impression que la patience est d'or en programmation... enfin bon me defend quand meme un peut :)

Bonne continuation, et encore merci.

signaler à un administrateur
Commentaire de Nifhell le 24/04/2006 16:32:11

Merci pour le "coup de coeur", t'aurais vu la version précédente de ce site, des caractères "orange" sur un fond "navy" => fabuleux!!!
Du coup j'ai arrété l'hémorragie :)

Pour ce qui est de la patience, ben "quand on aime on ne compte pas", ben là c'est un peu pareil... (esperons que ma petite amie ne lise jamais ça!!! Bienvenue à geekland, et fier de l'être!!! LOL)

signaler à un administrateur
Commentaire de coucou747 le 24/04/2006 17:20:18

"Bienvenue à geekland, et fier de l'être!!! LOL)"=> sur tes 7 messages, t'en as pas de posté à 4h du mat ^^

signaler à un administrateur
Commentaire de Anthomicro le 24/04/2006 19:04:23

en même temps qui sait, c'est l'heure du serveur, peut-être que chez lui il est 4 heures du mat si il n'est pas en france ^^

signaler à un administrateur
Commentaire de Nifhell le 26/04/2006 12:47:52

Bon,
j'ai trouvé un bug... la méthode CSuDoKuSquare::isValid() ne renvoie rien...
Je ferai une MAJ dés que j'aurais corrigé...

signaler à un administrateur
Commentaire de spyro666 le 05/05/2006 09:24:08

Absolument impressionnant :|

signaler à un administrateur
Commentaire de CodaVirgule le 18/06/2006 11:56:13

Bravo.
Merci pour cette source.

signaler à un administrateur
Commentaire de fudark le 24/08/2006 13:06:03

il m'a rendu une error:
Fatal error: Call to undefined method CSuDoKu::toCTable() in C:\AppServ\www\test\csudoku.php on line 486
et donc, qu'est ce que je pourais faire

signaler à un administrateur
Commentaire de blolenain le 25/08/2006 17:48:40

Parse error: syntax error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or '}' in /mnt/108/sdc/8/4/rdv.blaireaux/jeux/sudoku/csudoku.php on line 15

Moi j'ai cette erreur là!
comment y remedier ?

signaler à un administrateur
Commentaire de malalam le 25/08/2006 17:52:46 administrateur CS

Blolenain => c'est un code PHP5. Tu utilises PHP4.

signaler à un administrateur
Commentaire de blolenain le 25/08/2006 17:54:24

Comment on fait pour le savoir ?

signaler à un administrateur
Commentaire de malalam le 25/08/2006 17:58:14 administrateur CS

phpinfo ();

signaler à un administrateur
Commentaire de blolenain le 25/08/2006 18:03:30

Ah oui effectivement !

signaler à un administrateur
Commentaire de malalam le 25/08/2006 18:09:09 administrateur CS

Il ne te reste plus qu'à installer PHP5 ;-)

signaler à un administrateur
Commentaire de blolenain le 25/08/2006 18:11:08

Oui mais là c'est free qui gère ^^

signaler à un administrateur
Commentaire de malalam le 25/08/2006 18:20:20 administrateur CS

Renomme tes pages en .php5 (essaye avec ce code-ci), t'auras une surprise... ;-)

signaler à un administrateur
Commentaire de malalam le 25/08/2006 18:21:36 administrateur CS

Je veux dire l'extension des pages, hein...évidemment.

signaler à un administrateur
Commentaire de blolenain le 26/08/2006 10:20:15

J'ai la même erreur que fudark maintenant!!

Fatal error: Call to undefined method CSuDoKu::toCTable() in /mnt/108/sdc/8/4/rdv.blaireaux/jeux/sudoku/csudoku.php5 on line 486

Si vous pouvez nous aider ...

signaler à un administrateur
Commentaire de Nifhell le 29/08/2006 13:19:23

Certes il y a une erreur, merci de me l'avoir signaler et je fais la mise à jour de suite.

Cependant as tu jeté un coup d'oeil à la ligne 486 du fichier csudoku.php5?

signaler à un administrateur
Commentaire de dorad le 01/09/2006 12:51:40

Bjr,
pour ma part, j utilise un algo different,
que je mettrais en ligne dés qu'il sera finalisé,

C.A.D. :

je me heurte comme toi, au fait qu il reste plusieurs
solutions à une grille donnée, et cela ne me convient pas,
je cherche la soluce dans les maths, notamment sur la definition des masques des chiffres affichés.


A++;  bon courage et bon php a tous



signaler à un administrateur
Commentaire de stqv le 22/10/2006 14:10:07

bonjour,

j'essaie d'integrer ton code sur mon srv local, et je rencontre un problème.

n'utilisant pas les sessions, je ne voi pas comment remplacer la ligne "$sudoku=unserialize($_SESSION['sudoku']);" dans mon code

signaler à un administrateur
Commentaire de athanor70 le 26/05/2007 01:09:43

petits problèmes :

- plusieurs solutions pour une grille
- niveau de difficulté créé par le nombre de cases vides, ce qui n'est pas une bonne façon de proceder. Le niveau de difficulté ne depend pas que de cela, mais plutot des methodes que doit employer le joueur pour trouver LA solution (t'en fais pas, j'ai mis des mois et des mois a trouver pour mon jeu sudoku imperator en delphi)

signaler à un administrateur
Commentaire de nlion le 03/09/2008 14:20:38 7/10

Entièrement d'accord avec athanor70. Dommage !

Une piste : faire un algo de résolution par méthode de déduction (raisonement humain) pour tester ta grille générée.

Les méthodes de résolutions utilisées peuvent être :
- valeurs interdites
- solitaire nu
- paires nues visibles
- paires nues cachées

On peut ajouter triples nues visibles et cachés, X-Wing et Swordfish. X-Wing et Swordfish risquent d'être un peu lourd pour du php... Je n'ai pas encore testé... Je ne sais pas trop comment m'y prendre pour le moment...

Le niveau de difficulté peut être ainsi déterminé par la méthode de résolution.

Je vois que le dernier post est de 2007. Peut-être que ton projet a évolué depuis ?

Voici ma version si tu veux t'en inspirer ;) et si tu as des idées...
http://www.phpcs.com/codes/PARTIE-SUDOKU-CHOISSISSEZ-DIMENSION-NIVEAU-DIFFICULTE-TYPE-GRILLE_46204.aspx

++

Ajouter un commentaire

Discussions en rapport avec ce code source dans le forum

differnec entre php 4 et php5 [ par hardelgylls ] Bonjourpetite question :j'ai passer un oral et l'examinateur m'a demandé quel était la différence entre php4 et php5. et la gros blanc, est ce que qqu Doc PHP5 sniff sniff [ par slhuilli ] Bonjour, Bonsoir,Je suis a la recherche d'un PDF qui recenserait les mots-clefs + explications (bref un bouquin complet) sur PHP 5 qui parait-il est Algorithme de tri ... [ par LocalStone ] Salut à tous ! Il y a peut-être 1 mois et demi, j'ai lu un article - ou plutôt un tutorial - sur comment mettre en place un algorithme de tri automati Class POO retourné le nom de l'objet [ par MeTh ] Bonjour,Comment retourné le nom de l'objet déclaré?exemple :$monobjet = new GridR();comment recuperé $monobjet dans ma class?Merci Pb passage PHP4 -> PHP5 [ par Galmiza ] Salut,J'ai acheté un bouquin pour débuter le PHP.J'ai suivi a la lettre les instructions du livre:-installer EasyPHP 1.7-installer PHP 5.0..-lancer Ea Cohabitation PHP4 PHP5 sur même serveur ! [ par Zacland ] Ce n'est pas une question, mais je me doute que certaines personnes veulent essayer de faire cohabiter 2 versions de PHP sur un même serveur Apache... Un caractére se trouve t'il dans ma chaîne... [ par juki_webmaster ] Bonsoir,Je travaille depuis 14h cette apres-midi sur une fonction alternative d'une fonction connu et disponible uniquement sur php5, je fait cette fo PHP5 en PHP3 [ par el shaddai ] J'ai développé une partie de site en PHP5. MAlheureusement , chargé chez FREE, ils n'utilisent que PHP3. Y a t-il une manip simple pour qur du PHP5 pa PHP5 et MySQL 4.1.7 [ par TMT ] J'ai installé PHP5 et MySQL sur mon Windows XP avec IIS. J'ai bien activé le module php_mysql dans le fichier php.ini Là mon problème est qu-à chaque php4 vers php5 [ par aurelielaugraud ] Bonjour, Je suis passée de php4 à php5 pour utiliser la librairie graphique GD. Seulement, un programme que j'avais précédemment faire refuse de fonct


Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Téléchargements

Logiciels à télécharger sur le même thème :

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,468 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.