Example #1
0
        /// <summary>
        /// Solve a Sudoku with the specified initial state (using the standard dot/number representation)
        /// Example: "...84...9..1.....58...2146.7.8....9...........5....3.1.2491...79.....5..3...84..."
        /// </summary>
        public List <SudokuBoard> Solve(string sdnot)
        {
            var solutions = new List <SudokuBoard>();

            int i, j, c, r, r2, dir, cand, min, hints = 0; // dir=1: forward; dir=-1: backtrack
            var sr        = new sbyte[729];
            var cr        = new sbyte[81];                 // sr[r]: # times the row is forbidden by others; cr[i]: row chosen at step i
            var sc        = new byte[324];                 // bit 1-7: # allowed choices; bit 8: the constraint has been used or not
            var cc        = new short[81];                 // cc[i]: col chosen at step i
            var sdnot_out = new char[81];

            for (r = 0; r < 729; ++r)
            {
                sr[r] = 0; // no row is forbidden
            }
            for (c = 0; c < 324; ++c)
            {
                sc[c] = unchecked (0 << 7 | 9); // 9 allowed choices; no constraint has been used
            }
            for (i = 0; i < 81; ++i)
            {
                int a = sdnot[i] >= '1' && sdnot[i] <= '9' ? sdnot[i] - '1' : -1; // number from -1 to 8
                if (a >= 0)
                {
                    UpdateStateVectors(sr, sc, i * 9 + a, 1);         // set the choice
                }
                if (a >= 0)
                {
                    ++hints;         // count the number of hints
                }
                cr[i]        = -1;
                cc[i]        = -1;
                sdnot_out[i] = sdnot[i];
            }

            for (i = 0, dir = 1, cand = 10 << 16 | 0; ;)
            {
                while (i >= 0 && i < 81 - hints)
                { // maximum 81-hints steps
                    if (dir == 1)
                    {
                        min   = cand >> 16;
                        cc[i] = unchecked ((short)(cand & 0xffff));
                        if (min > 1)
                        {
                            for (c = 0; c < 324; ++c)
                            {
                                if (sc[c] < min)
                                {
                                    min   = sc[c];
                                    cc[i] = (short)c; // choose the top constraint
                                    if (min <= 1)
                                    {
                                        break;           // this is for acceleration; slower without this line
                                    }
                                }
                            }
                        }
                        if (min == 0 || min == 10)
                        {
                            cr[i--] = unchecked ((sbyte)(dir = -1)); // backtrack
                        }
                    }
                    c = cc[i];
                    if (dir == -1 && cr[i] >= 0)
                    {
                        UpdateStateVectors(sr, sc, _aux.r[c, cr[i]], -1); // revert the choice
                    }
                    for (r2 = cr[i] + 1; r2 < 9; ++r2)                    // search for the choice to make
                    {
                        if (sr[_aux.r[c, r2]] == 0)
                        {
                            break;                         // found if the state equals 0
                        }
                    }
                    if (r2 < 9)
                    {
                        cand    = UpdateStateVectors(sr, sc, _aux.r[c, r2], 1); // set the choice
                        cr[i++] = unchecked ((sbyte)r2); dir = 1;               // moving forward
                    }
                    else
                    {
                        cr[i--] = unchecked ((sbyte)(dir = -1)); // backtrack
                    }
                }

                if (i < 0)
                {
                    break;
                }

                for (j = 0; j < i; ++j)
                {
                    r = _aux.r[cc[j], cr[j]];

                    sdnot_out[r / 9] = (char)(r % 9 + '1'); // print
                }

                var solution = new SudokuBoard(new string(sdnot_out));

                solutions.Add(solution);
                if (solutions.Count >= _maxSolutions)
                {
                    break;
                }

                --i; dir = -1; // backtrack
            }
            return(solutions); // return the number of solutions
        }
        /// <summary>
        /// Create a game with the specified number of given cells.
        /// There is no quarantee that a low number of givens is achievable.
        /// You can increase the probability to get a low number of givens, by increasing the maximum number of attempts.
        /// The actual number of givens can be retrieved from the returned game object.
        /// </summary>
        public static SudokuGame Create(int givensCount, int maxAttempts = 2000)
        {
            // Start with a random finished board
            // and use it as the initial board
            var solution     = RandomFinishedBoard();
            var initialBoard = new SudokuBoard(solution); // start with finished

            var cells = initialBoard.Cells;

            // Create a solver that returns 2 solutions at most
            var solver = new SudokuSolver(maxSolutions: 2);

            // Start Removing Numbers one by one from the cells of the initial board

            // A higher number of attempts will end up removing more numbers from the grid
            //Potentially resulting in more difficiult grids to solve!
            var rnd         = new Random();
            var cellsFilled = 81;
            int attempt     = 0;

            while (true)
            {
                //  Select a random cell that is not already empty
                var cell = rnd.Next(0, cellsFilled);
                int i    = 0;
                var row  = -1;
                var col  = -1;
                for (int r = 0; r < 9; r++)
                {
                    for (int c = 0; c < 9; c++)
                    {
                        if (cells[r, c] != 0)
                        {
                            if (cell == i++)
                            {
                                row = r;
                                col = c;
                                break;
                            }
                        }
                    }
                }

                if (row == -1)
                {
                    // no empty cell to fill
                    return(new SudokuGame(initialBoard, solution));
                }

                //  Remember its cell value in case we need to put it back
                var backup = cells[row, col];
                cells[row, col] = 0;
                cellsFilled--;

                //  Count the number of solutions that this grid has (using a backtracking approach implemented Solve())
                //  If the number of solution is different from 1 then we need to cancel the change
                //  by putting the value we took away back in the grid
                if (solver.Solve(initialBoard).Count != 1)
                {
                    cells[row, col] = backup;
                    cellsFilled++;
                }

                if (cellsFilled <= givensCount)
                {
                    break;
                }

                attempt++;

                if (attempt >= maxAttempts)
                {
                    break;
                }
            }

            Console.WriteLine($"Generated in {attempt} attempts, with {cellsFilled} cells left");
            return(new SudokuGame(initialBoard, solution));
        }
Example #3
0
 /// <summary>
 /// Solve a Sudoku with the specified initial state
 /// </summary>
 public List <SudokuBoard> Solve(SudokuBoard initialBoard)
 {
     return(Solve(initialBoard.SudokuNotation));
 }
Example #4
0
 public SudokuGame(SudokuBoard initialBoard, SudokuBoard finishedBoard)
 {
     InitialBoard = initialBoard;
     Solution     = finishedBoard;
 }