Click here to Skip to main content
Click here to Skip to main content

Sudoku (Suduku) Solver

By , 17 Jan 2006
 

Introduction

After solving several Sudoku problems from newspapers and magazines, I started thinking about the algorithm one would use to solve these puzzles on the computer.

Background

The premise is very straightforward: starting with a partially filled in grid, the remaining blank cells must be filled in so that every row, every column, and every 3x3 box contains the digits 1 through 9. The source code attached includes the project that when run will generate a default 9x9 grid and accepts user input of the known cells. After filling in the cells, clicking "Calculate" will start the computation, and if one is found, it is displayed.

In addition to the standard 9x9 grids that have the unique property of 3x3 subgroups that must also contain the digits 1 through 9, I wanted to devise an algorithm that was generic enough to handle a grid of any size. These custom grids, if they are of a size that is a perfect square (1, 4, 9, 16, etc.), will compute a subgroup property for grids of the size of the square (1, 2, 3, 4, etc.). So in a 16x16 grid, each 4x4 subgrid must contain the digits 1 through 16, in addition to the requirement that these digits be present in every row and column of the main grid. Any grid that is not sized based on a perfect square will only compute a solution based on the rows and columns.

To facilitate testing grids that are of sizes besides 9x9, the included application supports the creation of a custom sized grid, capable of solving any size "Doku" problem.

Using the code

The code is broken into two classes, DukuForm, which is the user interface for this application, and DukuGrid, which is the class that contains the doku computation algorithm. The code is written for .NET 2.0, though no new types were leveraged, so it should be fairly straightforward to get this to run in 1.1.

Compute method

This is the method that does most of the work. Regardless of the size of the grid that the DukuGrid class was created with, if this method is called with 0,0 as starting parameters, the entire grid will be recursively computed. The additional parameter, which should only ever be set to true in the case that the grid was based on a perfect square dimension, will determine whether the grid enforces the additional constraint of having to detect the smaller subgrids.

/// <summary>
/// Continues the computation of the answer
/// to the Duku grid at a specific cell. This
/// function is used recursively to compute the entire grid.
/// </summary>
/// <param name="applyPerfectSquares">If true, this will apply
/// the rule of the perfect squares to the Grid. 
/// This should only be used on a grid of perfect square dimensions
/// (1, 4, 9, 16, etc.). This will enforce an additional rule of 
/// having each sub square grid contain the numbers 1-n.
/// In all other cases only rows and columns are checked</param>
/// <returns></returns>
public bool Compute(int row, int col, bool applyPerfectSquares)
{
    // determine the next row/col
    int nNextRow = row;
    int nNextCol = col;
    bool isLastCell = !NextRowCol(ref nNextRow, ref nNextCol);
    
    // if we are on a locked cell, move to the next cell
    if (statusGrid[row][col] == DukuStatus.LOCKED)
    {
        // deterministic step. If we've reached the end of 
        // our grid and it is locked, we have success
        if (isLastCell)
            return true;

        return Compute(nNextRow, nNextCol, applyPerfectSquares);
   }

    // determine the possibilities for this cell
    ArrayList possibilities = new ArrayList(nRowsColumns);
    
    // loop for each numeric value it could be
    for (int val = 1; val <= nRowsColumns; val++)
    {
        bool bFound = false;

        for (int rowIdx = 0; (rowIdx < nRowsColumns) && 
                                      (!bFound); rowIdx++)
        {
            if (numericGrid[rowIdx][col] == val)
            {
                bFound = true;
            }
        }

        for (int colIdx = 0; (colIdx < nRowsColumns) && 
                                     (!bFound); colIdx++)
        {
            if (numericGrid[row][colIdx] == val)
            {
                bFound = true;
            }
        }

        // this will apply the rule of perfect squares 
        if (applyPerfectSquares)
        {
            int subGridRowMax = 
              (nSubGridSize * ((row / nSubGridSize) + 1)) - 1;
            int subGridColMax = 
              (nSubGridSize * ((col / nSubGridSize) + 1)) - 1;

            for (int x = subGridRowMax - (nSubGridSize - 1); 
                                      x <= subGridRowMax; x++)
                for (int y = subGridColMax - (nSubGridSize - 1); 
                                         y <= subGridColMax; y++)
                    if(numericGrid[x][y] == val)
                        bFound = true;
        }

        // if the value was not already used, add it to the list
        if (!bFound)
            possibilities.Add(val);

    }

    
    // deterministic step. If we've reached the end of our 
    // grid and we have an option, set it and we are done
    if (isLastCell && possibilities.Count > 0)
    {
        numericGrid[row][col] = (int)possibilities[0];
        return true;
    }

    // now we have our list of possible values. 
    // Loop through, setting the value
    // and calling Compute on the next cell
    foreach (int val in possibilities)
    {
        numericGrid[row][col] = val;
        if (Compute(nNextRow, nNextCol, applyPerfectSquares))
            return true;
    }

    numericGrid[row][col] = (int)DukuValues.EMPTY;
    return false;
}

Points of interest

This turned out to be a decent example of dynamic user interface creation using C# and Windows Forms. The doku algorithm I created relies heavily on recursion in C#, and passes grids of data by reference in order to keep memory usage low.

History

This is the initial version of this code. I hope to add optimization in future to quickly detect the unsolvable grids. I may multi-thread it, so the UI can be updated with the status during longer computations.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Craig Spitzkoff
Web Developer
United States United States
Member
Craig currently works as the VP of Development at Raizlabs Corporation, a User Interface and Design consulting firm based in Brookline, MA. Previous to this Craig spent time at various companies in the defense industry such as Lockheed Martin, BAE Systems and Tybrin, where he worked on PC based Mission Planning software.
 
As a side project, Craig founded JobVent.com, a site where people can post reviews about their jobs and employers.
 
Craig graduated from Tufts University in 2000 with a B.S. in Computer Science and a minor in Multimedia Arts. Craig completed his M.B.A at Northeastern University in 2003.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralLove SudukumemberVandretta15 Jun '07 - 20:19 
This game really helps when I'm stuck on those newspaper ones that are REALLY hard!Big Grin | :-D
 
GOOD Job on the algorithm and .Net Implementation!Smile | :)
 
Thank you Big Grin | :-D
GeneralBeautiful SudokumemberBilloKhan18 Jan '07 - 10:36 
I must say that I have not seen such a beautiful Sudoku program yet. God bless you and keep good work.
Laugh | :laugh:
Agha Khan
 
Agha Khan
Generalawesomememberdwight017 Jan '06 - 20:35 
!
Generaloptimize ResetchoicememberGilbert Chauvaux14 Jan '06 - 8:12 
the "ResetChoicesPast" method does a lot to much for resetting cells after a try
there is only one cell to reset (or none if there was no possibility)
 
you just have to write , before the "return false" at the end of "Compute()" :
 
numericGrid[row][col] = (int)DukuValues.EMPTY;
 
and suppress the preceding "ResetChoicesPast()" call
the program works fine like that (or did I miss something ?)
 

 

 
Gilbert Chauvaux
GeneralRe: optimize ResetchoicememberCraig Spitzkoff14 Jan '06 - 9:31 
You are absolutely right; thank you for finding this. The ResetChoicesPast is completely unnecessary. As you pointed out, if you simply reset the current value (instead of all modified values) on the false return of the recursive step, you’ll be able to prevent all the necessary overhead of searching the grid every step for modified values.
 
I will submit an update to the article.
 
Thanks again!

GeneralBrute force--not elegentmemberWill Gray10 Jan '06 - 10:10 
Any 2nd semester CS student could write a script that can solve a Sudoku puzzle using brute force. The good programs HELP you solve the puzzle without giving it away. My preference, Simple Sudoku, will give you a hint on the next move, highlighting the cells and naming the technique (it doesn't just tell you the answer to a cell).
 
Your article should focus on the dynamic GUI creation instead of the computation.
GeneralRe: Brute force--not elegentmemberCraig Spitzkoff10 Jan '06 - 11:16 
You've missed the point.
 
Yes, there are programs out there, both client and web based, that use different methods to solve Sudoku problems. Some, as you say, will even give you a hint on the next move.
 
That was not the point of this article. I don't care about "hints", or not giving away the solution to the puzzle, or being elegant. This is not the program for someone who wants a "hint". This is for someone who wants to see a minimal amount of source code capable of solving a Sudoku.
 
I wanted to illustrate the mechanics of performing a recursive detection of the answer to a Suduku problem. Sure, a "2nd semester CS student" could have written this. Now they don't have to. Plus they have the added benefit of seeing how it would be written to solve a Sudoku of any variable size, not just the standard 9x9 grid.
 
If someone wants a "hint", they shouldn't be looking at source code; they should be downloading the other application you mentioned.[^]
 
Also, for those interested in more logic behind Sudoku grids, this could probably be a useful site.[^] None of the additional logic discussed there was used in this article though, because the goal of this artilcle was to show the source code, regardless of logic beyond row, column and subgrid, to force a solution to a grid.
 

 
-- modified at 10:13 Monday 16th January, 2006
GeneralRe: You've missed the pointmemberWill Gray10 Jan '06 - 11:42 
How do you know it's the minimal required? What about doing it the fastest?
 
I think this is just a basic programming exercise--there is nothing novel or interesting involved.
GeneralRe: You've missed the pointmembero_pontios10 Jan '06 - 12:56 
Will Gray wrote:
I think this is just a basic programming exercise--there is nothing novel or interesting involved.

 
ow well you'll get over it

GeneralRe: You've missed the pointmemberArbitrary Programmer10 Jan '06 - 14:09 
Will Gray wrote:
I think this is just a basic programming exercise

 
And Will Gray, I think you're just a basic idiot. If you don't have anything nice to say....
 
In other words, shut up.Big Grin | :-D

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 18 Jan 2006
Article Copyright 2006 by Craig Spitzkoff
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid