Esempio n. 1
0
        /// <summary>Generates a random Sudoku puzzle.</summary>
        /// <returns>The generated results.</returns>
        private static SolverResults GenerateOne(GeneratorOptions options)
        {
            // Generate a full solution randomly, using the solver to solve a completely empty grid.
            // For this, we'll use the elimination techniques that yield fast solving.
            PuzzleState   solvedState   = new PuzzleState();
            SolverOptions solverOptions = new SolverOptions();

            solverOptions.MaximumSolutionsToFind = 1;
            solverOptions.EliminationTechniques  = new List <EliminationTechnique> {
                new NakedSingleTechnique()
            };
            solverOptions.AllowBruteForce = true;
            SolverResults newSolution = Solver.Solve(solvedState, solverOptions);

            // Create options to use for removing filled cells from the complete solution.
            // MaximumSolutionsToFind is set to 2 so that we look for more than 1, but there's no
            // need in continuing once we know there's more than 1, so 2 is a find value to use.
            solverOptions.MaximumSolutionsToFind = 2;
            solverOptions.AllowBruteForce        =
                !options.MaximumNumberOfDecisionPoints.HasValue || options.MaximumNumberOfDecisionPoints > 0;
            solverOptions.EliminationTechniques = solverOptions.AllowBruteForce ?
                                                  new List <EliminationTechnique> {
                new NakedSingleTechnique()
            } : options.Techniques;                                                              // For perf: if brute force is allowed, techniques don't matter!

            // Now that we have a full solution, we want to randomly remove values from cells
            // until we get to a point where there is not a unique solution for the puzzle.  The
            // last puzzle state that did have a unique solution can then be used.
            PuzzleState newPuzzle = newSolution.Puzzle;

            // Get a random ordering of the cells in which to test their removal
            Point [] filledCells = GetRandomCellOrdering(newPuzzle);

            // Do we want to ensure symmetry?
            int filledCellCount = options.EnsureSymmetry && (filledCells.Length % 2 != 0) ? filledCells.Length - 1 : filledCells.Length;

            // If ensuring symmetry...
            if (options.EnsureSymmetry)
            {
                // Find the middle cell and put it at the end of the ordering
                for (int i = 0; i < filledCells.Length - 1; i++)
                {
                    Point p = filledCells[i];
                    if (p.X == newPuzzle.GridSize - p.X - 1 &&
                        p.Y == newPuzzle.GridSize - p.Y - 1)
                    {
                        Point temp = filledCells[i];
                        filledCells[i] = filledCells[filledCells.Length - 1];
                        filledCells[filledCells.Length - 1] = temp;
                    }
                }

                // Modify the random ordering so that paired symmetric cells are next to each other
                // i.e. filledCells[i] and filledCells[i+1] are symmetric pairs
                for (int i = 0; i < filledCells.Length - 1; i += 2)
                {
                    Point p  = filledCells[i];
                    Point sp = new Point(newPuzzle.GridSize - p.X - 1, newPuzzle.GridSize - p.Y - 1);
                    for (int j = i + 1; j < filledCells.Length; j++)
                    {
                        if (filledCells[j].Equals(sp))
                        {
                            Point temp = filledCells[i + 1];
                            filledCells[i + 1] = filledCells[j];
                            filledCells[j]     = temp;
                            break;
                        }
                    }
                }

                // In the order of the array, try to remove each pair from the puzzle and see if it's
                // still solvable and in a valid way.  If it is, greedily leave those cells out of the puzzle.
                // Otherwise, skip them.
                byte [] oldValues = new byte[2];
                for (int filledCellNum = 0;
                     filledCellNum < filledCellCount && newPuzzle.NumberOfFilledCells > options.MinimumFilledCells;
                     filledCellNum += 2)
                {
                    // Store the old value so we can put it back if necessary,
                    // then wipe it out of the cell
                    oldValues[0] = newPuzzle[filledCells[filledCellNum]].Value;
                    oldValues[1] = newPuzzle[filledCells[filledCellNum + 1]].Value;
                    newPuzzle[filledCells[filledCellNum]]     = null;
                    newPuzzle[filledCells[filledCellNum + 1]] = null;

                    // Check to see whether removing it left us in a good position (i.e. a
                    // single-solution puzzle that doesn't violate any of the generation options)
                    SolverResults newResults = Solver.Solve(newPuzzle, solverOptions);
                    if (!IsValidRemoval(newPuzzle, newResults, options))
                    {
                        newPuzzle[filledCells[filledCellNum]]     = oldValues[0];
                        newPuzzle[filledCells[filledCellNum + 1]] = oldValues[1];
                    }
                }

                // If there are an odd number of cells in the puzzle (which there will be
                // as everything we're doing is 9x9, 81 cells), try to remove the odd
                // cell that doesn't have a pairing.  This will be the middle cell.
                if (filledCells.Length % 2 != 0)
                {
                    // Store the old value so we can put it back if necessary,
                    // then wipe it out of the cell
                    int  filledCellNum = filledCells.Length - 1;
                    byte oldValue      = newPuzzle[filledCells[filledCellNum]].Value;
                    newPuzzle[filledCells[filledCellNum]] = null;

                    // Check to see whether removing it left us in a good position (i.e. a
                    // single-solution puzzle that doesn't violate any of the generation options)
                    SolverResults newResults = Solver.Solve(newPuzzle, solverOptions);
                    if (!IsValidRemoval(newPuzzle, newResults, options))
                    {
                        newPuzzle[filledCells[filledCellNum]] = oldValue;
                    }
                }
            }
            // otherwise, it's much easier.
            else
            {
                // Look at each cell in the random ordering.  Try to remove it.
                // If it works to remove it, do so greedily.  Otherwise, skip it.
                for (int filledCellNum = 0;
                     filledCellNum < filledCellCount && newPuzzle.NumberOfFilledCells > options.MinimumFilledCells;
                     filledCellNum++)
                {
                    // Store the old value so we can put it back if necessary,
                    // then wipe it out of the cell
                    byte oldValue = newPuzzle[filledCells[filledCellNum]].Value;
                    newPuzzle[filledCells[filledCellNum]] = null;

                    // Check to see whether removing it left us in a good position (i.e. a
                    // single-solution puzzle that doesn't violate any of the generation options)
                    SolverResults newResults = Solver.Solve(newPuzzle, solverOptions);
                    if (!IsValidRemoval(newPuzzle, newResults, options))
                    {
                        newPuzzle[filledCells[filledCellNum]] = oldValue;
                    }
                }
            }

            // Make sure to now use the techniques specified by the user to score this thing
            solverOptions.EliminationTechniques = options.Techniques;
            SolverResults finalResult = Solver.Solve(newPuzzle, solverOptions);

            // Return the best puzzle we could come up with
            newPuzzle.Difficulty = options.Difficulty;
            return(new SolverResults(PuzzleStatus.Solved, newPuzzle, finalResult.NumberOfDecisionPoints, finalResult.UseOfTechniques));
        }