[php5][poo][jeux] sudoku

Soyez le premier à donner votre avis sur cette source.

Vue 15 547 fois - Téléchargée 2 010 fois

Description

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 / Exemple :


<?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.

Codes Sources

A voir également

Ajouter un commentaire

Commentaires

Messages postés
12
Date d'inscription
vendredi 10 septembre 2004
Statut
Membre
Dernière intervention
1 mars 2009

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

++
Messages postés
3
Date d'inscription
samedi 26 mai 2007
Statut
Membre
Dernière intervention
26 mai 2007

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)
Messages postés
4
Date d'inscription
lundi 24 février 2003
Statut
Membre
Dernière intervention
22 octobre 2006

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
Messages postés
1
Date d'inscription
dimanche 20 juillet 2003
Statut
Membre
Dernière intervention
1 septembre 2006

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
Messages postés
48
Date d'inscription
mardi 17 février 2004
Statut
Membre
Dernière intervention
23 avril 2006

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?
Afficher les 36 commentaires

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.