Sudoku Programmers Forum Index

 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister   ProfileProfile   Log inLog in          Games  Calendar

Log in to check your private messagesLog in to check your private messages   

Sudoku implementation of DLX in PHP 5 objects

 
Post new topic   Reply to topic    Sudoku Programmers Forum Index -> Programming sudoku
View previous topic :: View next topic  
Author Message
Ruud
Site Admin
Joined: 17 Sep 2005
Posts: 708
:
Location: Netherlands

Items
PostPosted: Mon Jul 24, 2006 1:51 am    Post subject: Sudoku implementation of DLX in PHP 5 objects Reply with quote

The many requests for PHP solver/generator code has prompted me to rewrite some of my core code in PHP and make it available through this forum to the rest of the world.

I have tested this code on PHP 5.1.1 and it works like a charm.

On PHP 4.3.10-16 it does not work. Anyone who can retrofit the code to work with PHP 4 is encouraged to do so and post the modified code below.

There are several trace calls in the code that I have commented out. They have been very useful for debugging purposes, but maybe you want to change something in the code and use them again, so I left them in the code. A few to show the solution were left to show where the good things happen.

The solver is scalable, but I have not tested all settings.

Because this subject is so well documented elsewhere, I have not commented each line, but I did use self documenting names thoughout the code.

The optimizations in the search for the best column have resulted in extended column size administration. The overhead is justified by the speed increase of the main code.

Comments and improvements are appreciated.

Code:
<html>
<head>
</head>
<body style="font-family:Verdana;font-size:12px;">
<h1>Sudoku DLX objects for PHP 5</h1>
<?php
///////////////////////////////////////////////////////////////////////////////////
//                        Sudoku DLX objects for PHP 5                           //
//                           This is free software                               //
// Author: Ruud van der Werf. Contact: ruud at sudocue dot net     July 23, 2006 //
// ----------------------------------------------------------------------------- //
// You can use it as you please, modify it until it breaks, or whatever you like //
// The software is written in the expectation that it may be useful, but there   //
// will be no guarantee that this will actually be the case. It may do nothing.  //
// The author is not responsible for any damage directly or indirectly caused    //
// by the use of this software.                                                  //
// Keep this notice attached to the software when you modify and copy it.        //
///////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////
// Utility function to output a trace message //
////////////////////////////////////////////////
function trace($message) { echo '<br>'.$message; }
////////////////////////////////////////////////////////////
// Base class for all header and detail nodes in the grid //
// all methods are implemented in the extended classes    //
////////////////////////////////////////////////////////////
class Node {
   var $NextInCol;
   var $PrevInCol;
   var $ColHeader;
   var $IsHeader;
}
////////////////////////////////////
// Candidate - acts as Row header //
////////////////////////////////////
class Candidate {
   var $FirstNode;
   var $Index;
   var $Digit;
   var $Enabled;
   var $Name;

   function Candidate(&$colHeaderCell,&$colHeaderRow,&$colHeaderCol,&$colHeaderBox,$digit) {
      $this->FirstNode = new GridNode($this,$colHeaderCell);
      $this->FirstNode->Append(new GridNode($this,$colHeaderRow));
      $this->FirstNode->Append(new GridNode($this,$colHeaderCol));
      $this->FirstNode->Append(new GridNode($this,$colHeaderBox));
      $this->Name = $colHeaderCell->Name.'Val'.$digit;
      $this->Index = $colHeaderCell->Index;
      $this->Digit = $digit;
      $this->Enabled = true;
      //trace($this->Name.' initialized.');
   }
   function TraceStructure() {
      $nd = &$this->FirstNode;
      trace($nd->PrevInRow->ColHeader->Name. ' <- '.$nd->ColHeader->Name.' -> '.$nd->NextInRow->ColHeader->Name);
      $nd = &$nd->NextInRow;
      trace($nd->PrevInRow->ColHeader->Name. ' <- '.$nd->ColHeader->Name.' -> '.$nd->NextInRow->ColHeader->Name);
      $nd = &$nd->NextInRow;
      trace($nd->PrevInRow->ColHeader->Name. ' <- '.$nd->ColHeader->Name.' -> '.$nd->NextInRow->ColHeader->Name);
      $nd = &$nd->NextInRow;
      trace($nd->PrevInRow->ColHeader->Name. ' <- '.$nd->ColHeader->Name.' -> '.$nd->NextInRow->ColHeader->Name);
   }
   function Disable() {
      if (!$this->Enabled) return;
      $this->Enabled = false;
      //trace('>'.$this->Name.' disable started.');
      $nd = &$this->FirstNode;
      do
      {
         $nd->Disable();
         $nd= &$nd->NextInRow;
      }
      while ($nd!==$this->FirstNode);
      //trace('>'.$this->Name.' disable finished.');
   }
   function Enable() {
      if ($this->Enabled) return;
      $this->Enabled = true;
      //trace('>'.$this->Name.' enable started.');
      $nd = &$this->FirstNode;
      do
      {
         $nd->ColHeader->Add($nd);
         $nd= &$nd->NextInRow;
      }
      while ($nd!==$this->FirstNode);
      //trace('>'.$this->Name.' enable finished.');
   }
}
///////////////////////////////////////////////////////////
// Grid Node - Each row has 4 and each column has base^2 //
///////////////////////////////////////////////////////////
class GridNode extends Node {
   var $Candidate;
   var $NextInRow;
   var $PrevInRow;
   var $Enabled;
   var $Covered;
   
   function GridNode(&$candidate,&$colHeader) {
      $this->Candidate = &$candidate;
      $this->ColHeader = &$colHeader;
      $this->IsHeader = false;
      $colHeader->Add($this);
      $this->NextInRow = &$this;
      $this->PrevInRow = &$this;
      $this->Enabled = false;
      $this->Covered = false;
   }
   function Append(&$newNode) {
      $newNode->PrevInRow = &$this->PrevInRow;
      $newNode->NextInRow = &$this;
      $this->PrevInRow->NextInRow = &$newNode;
      $this->PrevInRow = &$newNode;
   }
   function RemoveFromColumn() {
      if ($this->Covered)
      {
         trace('Node in '.$this->ColHeader->Name.' already covered.');
         return;
      }
      $this->NextInCol->PrevInCol = &$this->PrevInCol;
      $this->PrevInCol->NextInCol = &$this->NextInCol;
      $this->ColHeader->Decrement();
      $this->Covered = true;
   }
   function Disable() {
      //trace('Node in '.$this->ColHeader->Name.' disabled.');

      $this->NextInCol->PrevInCol = &$this->PrevInCol;
      $this->PrevInCol->NextInCol = &$this->NextInCol;
      $this->NextInCol = &$this;
      $this->PrevInCol = &$this;
      $this->ColHeader->Decrement();
      $this->Enabled = false;
   }
   function RestoreInColumn() {
      if (!$this->Covered)
      {
         trace('Node in '.$this->ColHeader->Name.' already uncovered.');
         return;
      }
      $this->NextInCol->PrevInCol = &$this;
      $this->PrevInCol->NextInCol = &$this;
      $this->ColHeader->Increment();
      $this->Covered = false;
   }
}
///////////////////////////////////////////////////
// Column Header class - Represents a constraint //
///////////////////////////////////////////////////
class ColHeader extends Node {
   var $Size;
   var $NextColumn;
   var $PrevColumn;
   var $Dancer;
   var $IsRoot;
   var $Type;
   var $Index1;
   var $Index2;
   var $Index;
   var $Name;
   var $Covered;

   function ColHeader(&$dancer,$type,$index1,$index2) {
      $this->Dancer = &$dancer;
      $this->Type = $type;     // 0=root, 1=Cell, 2=Row, 3=Column, 4=Box
      if ($type==0)
      {
         $this->IsRoot = true;
         $this->NextColumn = &$this;
         $this->PrevColumn = &$this;
         $this->Name = 'root node';
      }
      else
      {
         $this->IsRoot = false;
         $this->Index1 = $index1; // Cell: Row index 1-9, House: Row/Col/Box index 1-9
         $this->Index2 = $index2; // Cell: Col index 1-9, House: Digit 1-9
         $this->Index = (($index1-1)*$dancer->Size) + ($index2-1);

         if ($type==1) $this->Name = 'Row'.$index1.'Col'.$index2;
         if ($type==2) $this->Name = 'Row'.$index1.'Val'.$index2;
         if ($type==3) $this->Name = 'Col'.$index1.'Val'.$index2;
         if ($type==4) $this->Name = 'Box'.$index1.'Val'.$index2;

         $this->IsHeader = true;
         $this->ColHeader = &$this;
         $this->NextInCol = &$this;
         $this->PrevInCol = &$this;

         $this->NextColumn = &$this->Dancer->Root;
         $this->PrevColumn = &$this->Dancer->Root->PrevColumn;
         $this->Dancer->Root->PrevColumn->NextColumn = &$this;
         $this->Dancer->Root->PrevColumn = &$this;
      }
      $this->Size = 0;
      $this->Covered = false;
   }
   function Add(&$node) {
      $node->NextInCol = &$this;
      $node->PrevInCol = &$this->PrevInCol;
      $this->PrevInCol->NextInCol = &$node;
      $this->PrevInCol = &$node;
      $node->Enabled = true;
      $this->Increment();
   }
   function Cover() {
      if ($this->Covered)
      {
         trace('Column '.$this->Name.' already covered.');
         return;
      }
      //trace('Covering column: '.$this->Name.' with size '.$this->Size);

      $this->NextColumn->PrevColumn = &$this->PrevColumn;
      $this->PrevColumn->NextColumn = &$this->NextColumn;
      $this->Dancer->CoverFromSize($this->Size);
      
      if ($this->Size>0)
      {
         for ($LoopNode=&$this->NextInCol;!$LoopNode->IsHeader;$LoopNode=&$LoopNode->NextInCol)
            for ($PeerNode=&$LoopNode->NextInRow; $PeerNode!==$LoopNode;$PeerNode=&$PeerNode->NextInRow)
               $PeerNode->RemoveFromColumn();
      }
      $this->Covered = true;
      //trace('Covered column: '.$this->Name);
   }
   function UnCover() {
      if (!$this->Covered)
      {
         trace('Column '.$this->Name.' is not covered.');
         return;
      }
      //trace('Uncovering column: '.$this->Name);

      if ($this->Size>0)
      {
         for ($LoopNode=&$this->PrevInCol;!$LoopNode->IsHeader;$LoopNode=&$LoopNode->PrevInCol)
            for ($PeerNode=&$LoopNode->PrevInRow; $PeerNode!==$LoopNode;$PeerNode=&$PeerNode->PrevInRow)
               $PeerNode->RestoreInColumn();
      }
      $this->NextColumn->PrevColumn = &$this;
      $this->PrevColumn->NextColumn = &$this;
      $this->Dancer->UncoverToSize($this->Size);
      $this->Covered = false;
   }
   function Increment() {
      $this->Size++;
      //trace('Column size incremented to '.$this->Size);
      $this->Dancer->ColSizeIncremented($this->Size);
   }
   function Decrement() {
      $this->Size--;
      //if ($this->Size==0) trace($this->Name.' size reduced to zero');
      //trace('Column size decremented to '.$this->Size);
      $this->Dancer->ColSizeDecremented($this->Size);
   }
}
//////////////////////////////
// DLX main object - Dancer //
//////////////////////////////
class Dancer {

   var $Base;
   var $Size;
   var $Area;
   var $Root;
   var $Cols;
   var $Rows;
   var $Sizes;
   var $BestSize;

   var $SolutionCount;
   var $SolutionLimit;
   var $Abort;
   var $Depth;
   var $Breath;
   var $BackTracks;
   var $SelectedRows;
   var $Givens;
   var $Solution;

   function Dancer($base) {
      $size = $base * $base;
      $area = $size * $size;
      $this->Base = $base;
      $this->Size = $size;
      $this->Area = $area;
      $this->Givens='';

      for ($n=0;$n<=$size;$n++) $this->Sizes[$n] = 0;
      for ($n=0;$n<=$area;$n++) { $this->Solution[$n] = 0; $this->Givens .= '0'; }
      $this->BestSize = 0;
      
      $this->Root = new ColHeader($this,0,0,0);
      for ($type=1;$type<=4;$type++)
      {
         for ($index1=1;$index1<=$size;$index1++)
         {
            for ($index2=1;$index2<=$size;$index2++)
            {
               $index = (($type-1)*$area)+(($index1-1)*$size)+($index2-1);
               $this->Cols[$index] = new ColHeader($this,$type,$index1,$index2);
               $this->Sizes[0]++;
            }
         }
      }
      //trace('columns initialized');
      for ($R=0;$R<$size;$R++)
      {
         $floor = intval($R / $base);
         for ($C=0;$C<$size;$C++)
         {
            $tower = intval($C / $base);
            $B = ($floor * $base) + $tower;
            $colHeaderCell = &$this->Cols[$R*$size+$C];
            
            for ($D=0;$D<$size;$D++)
            {
               $colHeaderRow = &$this->Cols[(1*$area)+$R*$size+$D];
               $colHeaderCol = &$this->Cols[(2*$area)+$C*$size+$D];
               $colHeaderBox = &$this->Cols[(3*$area)+$B*$size+$D];
               $rowIndex = ($R*$area)+($C*$size)+$D;
               
               $this->Rows[$rowIndex] = new Candidate($colHeaderCell,$colHeaderRow,$colHeaderCol,$colHeaderBox,$D+1);
            }
         }
      }
   }

   function CoverFromSize($size) {
      $this->Sizes[$size]--;
      if ($this->Sizes[$size]==0) if ($this->BestSize==$size) while ($this->BestSize<$this->Size && $this->Sizes[$this->BestSize]==0) $this->BestSize++;
   }
   function UncoverToSize($size) {
      $this->Sizes[$size]++;
      if ($this->BestSize>$size) $this->BestSize=$size;
   }

   function ColSizeIncremented($new) {
      $old = $new-1;
      $this->Sizes[$old]--;
      if($this->Sizes[$old]==0) if ($this->BestSize==$old) $this->BestSize=$new;
      $this->Sizes[$new]++;
   }
   function ColSizeDecremented($new) {
      $old = $new+1;
      $this->Sizes[$old]--;
      $this->Sizes[$new]++;
      if ($this->BestSize>$new) $this->BestSize=$new;
   }

   function SetGivens($givens) {
      $this->Givens = $givens;
      for ($n=0;$n<strlen($givens);$n++) if (intval(substr($givens,$n,1))>0) $this->Place($n,intval(substr($givens,$n,1)));
   }
   function RemoveGivens() {
      for ($n=strlen($this->Givens)-1;$n>=0;$n--) if (intval(substr($this->Givens,$n,1))>0) $this->Erase($n);
   }
   function Place($index,$placedDigit) {
      $R = intval($index / $this->Size) + 1;
      $C = $index - (($R-1)*$this->Size) + 1;
      //trace('Given digit '.$placedDigit.' in R'.$R.'C'.$C);
      for ($otherDigit=1;$otherDigit<=$this->Size;$otherDigit++)
      {
         if ($otherDigit != $placedDigit)
         {
            $rowIndex = ($index*$this->Size)+$otherDigit-1;
            $rowHeader = &$this->Rows[$rowIndex];
            $rowHeader->Disable();
         }
      }
   }
   function Erase($index) {
      $R = intval($index / $this->Size) + 1;
      $C = $index - (($R-1)*$this->Size) + 1;
      //trace('Clearing R'.$R.'C'.$C);
      for ($digit=0;$digit<$this->Size;$digit++)
      {
         $rowIndex = ($index*$this->Size)+$digit;
         $rowHeader = &$this->Rows[$rowIndex];
         $rowHeader->Enable();
      }
   }

   function Solve($limit) {
      
      $this->SolutionLimit = $limit;
      $this->SolutionCount = 0;
      $this->Depth = 0;
      $this->Breath = 0;
      $this->BackTracks = 0;
      $this->Abort = false;
      $this->Recurse();
   }
   function AllColumnsCovered() {

      $this->SolutionCount++;
      if ($this->SolutionCount==1)
      {
         for ($n=0;$n<$this->Depth;$n++) $this->Solution[$this->SelectedRows[$n]->Index] = $this->SelectedRows[$n]->Digit;
         $sol = "";
         for ($n=0;$n<81;$n++) $sol .= $this->Solution[$n];
         trace('<br>Solution: '.$sol);
         trace('Breath: '.$this->Breath);
         trace('Backtracked: '.$this->BackTracks);
      }
      else
      {
         trace('<br>Solution #'.$this->SolutionCount.' found.<br>');
         if ($this->SolutionLimit>0) if ($this->SolutionCount>$this->SolutionLimit) $this->Abort = true;
      }
   }
   function Recurse() {
      //trace('&nbsp;');
      //for ($n=0;$n<=$this->Size;$n++) trace('Size '.$n.': '.$this->Sizes[$n]);

      // End condition: All columns covered
      if ($this->Root->NextColumn->IsRoot)
      {
         $this->AllColumnsCovered();
         return;
      }
      // Find the best column

      //trace('Best size is now: '.$this->BestSize);
      
      $SelectedCol = &$this->Root->NextColumn;
      while ($SelectedCol->Size>$this->BestSize) $SelectedCol = &$SelectedCol->NextColumn;

      // Main DLX function

      $this->Breath += $SelectedCol->Size;
      $SelectedCol->Cover();
      
      for ($LoopNode=&$SelectedCol->NextInCol;!$LoopNode->IsHeader;$LoopNode=&$LoopNode->NextInCol)
      {
         //trace('<br>Selected '.$LoopNode->Candidate->Name.'<br>');
         for ($PeerNode=&$LoopNode->NextInRow; $PeerNode!==$LoopNode;$PeerNode=&$PeerNode->NextInRow)
            $PeerNode->ColHeader->Cover();

         $this->SelectedRows[$this->Depth]=&$LoopNode->Candidate;
         $this->Depth++;
         //trace('<br>Depth increased to '.$this->Depth);
         $this->Recurse();
         $this->Depth--;
         $this->SelectedRows[$this->Depth]=null;
         //trace('Depth decreased to '.$this->Depth.'<br>');
            
         for ($PeerNode=&$LoopNode->PrevInRow; $PeerNode!==$LoopNode;$PeerNode=&$PeerNode->PrevInRow)
            $PeerNode->ColHeader->Uncover();

         $this->BackTracks++;
         //trace('<br>Deselected '.$LoopNode->Candidate->Name.'<br>');
         if ($this->Abort) break;
      }
      
      $SelectedCol->Uncover();
      $this->Breath -= $SelectedCol->Size;
      //trace('&nbsp;');
   }
}

//main code line

$dancer = new Dancer(3);
$dancer->SetGivens('901000802002000700400000005640209071080703020000405000000907000710000083004000200');
echo '<p>Puzzle: '.$dancer->Givens.'</p>';
$dancer->Solve(1);
if ($dancer->SolutionCount!=1) trace('Puzzle has '.$dancer->SolutionCount.' solutions.');
$dancer->RemoveGivens();
?>
</body>
</html>

The sample puzzle is todays nightmare. Finally a piece of code that helps you solve it Laughing

Ruud.
_________________
Meet me at sudocue.net
Back to top
View user's profile Send private message Visit poster's website
lath

Joined: 20 Jul 2006
Posts: 3
:

Items
PostPosted: Tue Jul 25, 2006 10:48 pm    Post subject: Reply with quote

Wow, thanks a ton! This will prove very useful.

So, correct me if I'm wrong, here's what I need to do:

I already have code that will generate completed puzzles.

1) Randomly remove a cell from the puzzle.
2) Run through $dancer->Solve
2a) If more than one solution, put number back and goto step 1 again.
3) Continue until x desired givens are left.

And I have a sudoku puzzle, right?

If I want to add different levels of difficulty, how can I tell which techniques are required to solve the puzzle?

Thanks.
Back to top
View user's profile Send private message
Ruud
Site Admin
Joined: 17 Sep 2005
Posts: 708
:
Location: Netherlands

Items
PostPosted: Tue Jul 25, 2006 11:46 pm    Post subject: Reply with quote

Your process sounds about right.

To add 180 degree symmetry, generate a random number N between 0 and 40, and clear cells N and 80-N,unless N=40.

You need a stop condition. Minimum number of clues is an option. Also create a mask for the clues that you tried to remove. Stop when all clues have been removed or tried.

Normally, difficulty rating is tested afterwards with a solver that uses human-style techniques.

This solver has a property that will show immediately whether the puzzle requires techniques beyond singles.

if ($dancer->Breath == 81) // only singles are required
if ($dancer->Breath > 81) // other techniques needed

There are a few rules of thumb, but you need to confirm this with a human-style solver. Use it as a kind of triage.

81 - Singles only
82-86 - Locked candidates
87-95 - Subsets and fish
96-120 - Multiple techniques
120-250 - + coloring, XY-chains
>250 - all kinds of weird crap

That's about it.

good luck!
Back to top
View user's profile Send private message Visit poster's website
humble_programmer

Joined: 27 Jun 2006
Posts: 69
:
Location: Colorado Springs, USA

Items
PostPosted: Thu Feb 01, 2007 3:19 pm    Post subject: Reply with quote

Can you elaborate on the purpose and logic of the CoverFromSize() and UncoverFromSize() functions in your PHP example? I'm trying to optimize my C# Dancing Links implementation, and suspect that I can gain some performance by looking harder at which column to choose. (Another "board member" rightly pointed out that the C# code I posted before Christmas is actually using Algorithm X instead of Dancing Links...oh, the shame!)
_________________
Cheers!
Humble Programmer
,,,^..^,,,
www.humble-programmer.com
Back to top
View user's profile Send private message Visit poster's website
Ruud
Site Admin
Joined: 17 Sep 2005
Posts: 708
:
Location: Netherlands

Items
PostPosted: Thu Feb 01, 2007 8:11 pm    Post subject: Reply with quote

The Sizes array keeps track of the number of remaining columns for each size. (ranging from 0 to 9)

ColSizeIncremented() and ColSizeDecremented() update this array when the column size is changed.

CoverFromSize() is called when a column is covered. Its size does not need to be zero at that point, so the Sizes element for the size of that column is decremented.

UncoverToSize() reverses this operation when the column is uncovered when the program backtracks (or tracks back?)

All 4 methods also process the effects for the BestSize variable. This variable is used in the recursive process to determine when to stop searching the columns. Once a column with size==BestSize is encountered, it is selected, because there will not be any columns with a smaller size.

This optimization works best if you mainly solve difficult puzzles. For easier Sudokus, it is quicker to stop the search after you found a column with size<=1.

Ruud
Back to top
View user's profile Send private message Visit poster's website
yAnTar

Joined: 16 Apr 2007
Posts: 2
:

Items
PostPosted: Mon Apr 16, 2007 2:20 pm    Post subject: Work not up to the end Reply with quote

Work not up to the end

When sudoku is -
901000802002000700400000005640209071080703020000405000000907000710000083004000200
Solution -
901304862832601700406800305640289071080703020000405038308907100710020083064108297
Back to top
View user's profile Send private message
yAnTar

Joined: 16 Apr 2007
Posts: 2
:

Items
PostPosted: Wed Apr 18, 2007 2:49 pm    Post subject: Reply with quote

Why ?
Is normal ?
I must solve by other methods to end or ... ?

Sorry of my english.
Back to top
View user's profile Send private message
Pat

Joined: 06 Sep 2006
Posts: 128
:

Items
PostPosted: Sun Apr 29, 2007 8:02 am    Post subject: re: Work not up to the end Reply with quote

yAnTar wrote:
Work not up to the end

When sudoku is -
Code:

 9 . 1 | . . . | 8 . 2
 . . 2 | . . . | 7 . .
 4 . . | . . . | . . 5
-------+-------+------
 6 4 . | 2 . 9 | . 7 1
 . 8 . | 7 . 3 | . 2 .
 . . . | 4 . 5 | . . .
-------+-------+------
 . . . | 9 . 7 | . . .
 7 1 . | . . . | . 8 3
 . . 4 | . . . | 2 . .



Solution -
Code:

 9 . 1 | 3 . 4 | 8 6 2
 8 3 2 | 6 . 1 | 7 . .
 4 . 6 | 8 . . | 3 . 5
-------+-------+------
 6 4 . | 2 8 9 | . 7 1
 . 8 . | 7 . 3 | . 2 .
 . . . | 4 . 5 | . 3 8
-------+-------+------
 3 . 8 | 9 . 7 | 1 . .
 7 1 . | . 2 . | . 8 3
 . 6 4 | 1 . 8 | 2 9 7



Why ?
Is normal ?
I must solve by other methods to end or ... ?

sorry but i don't understand your question,
at the point you posted there's nothing left to do but "hidden singles" --
Back to top
View user's profile Send private message Visit poster's website
Kaimi

Joined: 23 May 2007
Posts: 5
:

Items
PostPosted: Fri May 25, 2007 8:21 am    Post subject: Reply with quote

there is a problem. dlx solver must find full solution if it exists, and in this case it exists. my version found it.
and there is another problem. when solving empty puzzle, solution 3 and 4 are equal. I found it out when tested my own implementation.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Sudoku Programmers Forum Index -> Programming sudoku All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
Sudoku Programmers topic RSS feed 


Powered by phpBB © 2001, 2005 phpBB Group

Igloo Theme Version 1.0 :: Created By: Andrew Charron