如何生成有效的数独板? [和难度等级]

时间:2012-12-22 10:14:31

标签: c# algorithm sudoku

我正在开发一个Sudoku程序。目前,我可以生成一个有效的数独板,但我不知道我是否生成了一个简单或困难的板。

这是我的方法:

我从生成全板开始。最后一块板是空的。

我开始使用全板的值填充21个随机单元格。

我找到了最终电路板的所有解决方案,并且对于每个电池,计算整个电路板解决方案的差异,选择最多的电路板并填充它。 (这个想法来自这里:How to generate Sudoku boards with unique solutions第一个答案)

这样做,直到你只有一个解决方案。

现在,我不知道21个随机细胞的这种方法是否合适。有时我会在前21个随机单元格之后有很多解决方案,其他时候会有一些。

目前,我的平均时间为:0,3秒生成最终板。

我想告诉我一个不同的快速方法,除了这个方法之外,还有21个随机单元格,也许我可以在那里排列董事会的难度。

谢谢!

2 个答案:

答案 0 :(得分:5)

看看这个代码实现了3个难度级别{Easy,Medium,Hard}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SuperSudoku
{
    public enum Difficulty
    {
        Easy, Medium, Hard
    }

    class PuzzleGenerator
    {
        private PuzzleSolver puzzleSolver;

        /// <summary>
        /// 
        /// </summary>
        public PuzzleGrid PermaGrid;

        /// <summary>
        /// 
        /// </summary>
        public PuzzleGrid SolutionGrid;

        /// <summary>
        /// 
        /// </summary>
        private Difficulty difficulty;

        /// <summary>
        /// This constructs a puzzle generator class.
        /// </summary>
        /// <param name="difficultyIn">The difficulty to generate a new puzzle.</param>
        public PuzzleGenerator(Difficulty difficultyIn)
        {
            puzzleSolver = new PuzzleSolver();
            difficulty = difficultyIn;
        }

        public PuzzleGrid InitGrid()
        {                 //Randomly fill in the first row and column of puzzlegrid
            PuzzleGrid tempGrid = new PuzzleGrid { };      //temporary grid to assign values into
            int row = 0;                           //variable for navigating 'rows'
            int col = 0;                        //variable for navigating 'columns'
            int newVal;                                  //value to place into grid
            bool solved;
            List<int> valueSet = new List<int>(Enumerable.Range(-9, 9));   //range 
                                     //of numbers that can be added to the grid
            List<int> valueSet2 = new List<int>(); //placeholder values in column 0
            Random rnd = new Random(); //random variable for choosing random number
            int randIndex = 0;       //index in valueSet/valueSet2 that is accessed
            randIndex = rnd.Next(0,8); //get a random number and place in grid(0,0)
            newVal = valueSet[randIndex]; 
            tempGrid.InitSetCell(row,col,newVal);
            valueSet.Remove(newVal);              //remove paced value from options
            for(row = 1; row < 9; row++)
            { //fills in column 0 with remaining possible values, storing in place-
              //holder as it goes so as to preserve when placing in row 0 later
                randIndex = rnd.Next(0,valueSet.Count);
                newVal = valueSet[randIndex];
                valueSet2.Add(newVal);
                valueSet.Remove(newVal);
                tempGrid.InitSetCell(row,col,newVal);
            }
               row = 0;                                               //reset row to 0
            for(col = 1; col < 3; col++)
            {        //fills in col 1,2 of row 0, checking that don't duplicate the
                                                  //values in rows 1,2 of col 0
                randIndex = rnd.Next(0,valueSet2.Count);
                newVal = valueSet2[randIndex];
                while((newVal == tempGrid.Grid[1,0]||(newVal == tempGrid.Grid[2,0])))
                {
                    randIndex = rnd.Next(0,valueSet2.Count);
                    newVal = valueSet2[randIndex];
                }
                valueSet2.Remove(newVal);
                tempGrid.InitSetCell(row,col,newVal);
            }
            for(col = 3; col < 9; col++)
            {           //fill in remainder of row 0 with remaining possible values
                randIndex = rnd.Next(0,valueSet2.Count);
                newVal = valueSet2[randIndex];
                valueSet2.Remove(newVal);
                tempGrid.InitSetCell(row,col,newVal);
            }
            do
            {
                puzzleSolver = new PuzzleSolver();
                puzzleSolver.SolveGrid((PuzzleGrid)tempGrid.Clone(), false); //Slv to fill remainder of grid
                SolutionGrid = puzzleSolver.SolutionGrid;
            } while (SolutionGrid == null || SolutionGrid.IsBlank());
            PermaGrid = Blanker(SolutionGrid);       //call Blanker to carry out the
            return PermaGrid;         //blanking of fileds,then return the grid to user to solve
        }
        //  Call SolveGrid to solve puzzlegrid
        //Store solved gamegrid as the correct solution in solutiongrid

        public PuzzleGrid Blanker(PuzzleGrid solvedGrid)
        {                          //enable blanking of squares based on difficulty
            PuzzleGrid tempGrid; 
            PuzzleGrid saveCopy;
                                        //temporary grids to save between tests
            bool unique = true;          //flag for if blanked form has unique soln
            int totalBlanks = 0;                          //count of current blanks
            int tries = 0;                  //count of tries to blank appropriately
            int desiredBlanks;            //amount of blanks desired via difficulty
            int symmetry = 0;                                       //symmetry type
            tempGrid = (PuzzleGrid)solvedGrid.Clone(); 
                                                //cloned input grid (no damage)
            Random rnd = new Random();         //allow for random number generation

            switch (difficulty)           //set desiredBlanks via chosen difficulty
            {
            case Difficulty.Easy: //easy difficulty
                desiredBlanks = 40;
                break;
            case Difficulty.Medium: //medium difficulty
                desiredBlanks = 45;
                break;
            case Difficulty.Hard: //hard difficulty
                desiredBlanks = 50;
                break;
            default: //easy difficulty
                desiredBlanks = 40;
                break;
            }

            symmetry = rnd.Next(0, 2);                   //Randomly select symmetry
            do
            {          //call RandomlyBlank() to blank random squares symmetrically
                saveCopy = (PuzzleGrid)tempGrid.Clone();     // in case undo needed
                tempGrid = RandomlyBlank(tempGrid, symmetry, ref totalBlanks);
                           //blanks 1 or 2 squares according to symmetry chosen
                puzzleSolver = new PuzzleSolver();
                unique = puzzleSolver.SolveGrid((PuzzleGrid)tempGrid.Clone(), true);         // will it solve uniquely?
                if(!unique)
                {
                    tempGrid = (PuzzleGrid)saveCopy.Clone();
                    tries++;
                }
            } while((totalBlanks < desiredBlanks) && (tries < 1000));
            solvedGrid = tempGrid;
            solvedGrid.Finish();
            return solvedGrid;
        }

        public PuzzleGrid RandomlyBlank(PuzzleGrid tempGrid, int sym, ref int blankCount)
        {
            //blank one or two squares(depending on if on center line) randomly
            Random rnd = new Random(); //allow random number generation
            int row = rnd.Next(0, 8); //choose randomly the row
            int column = rnd.Next(0, 8); //and column of cell to blank
            while (tempGrid.Grid[row, column] == 0) //don't blank a blank cell
            {
                row = rnd.Next(0, 8);
                column = rnd.Next(0, 8);
            }
            tempGrid.InitSetCell(row, column, 0); //clear chosen cell
            blankCount++; //increment the count of blanks
            switch (sym)
            {
                    //based on symmetry, blank a second cell
                case 0: //vertical symmetry
                    if (tempGrid.Grid[row, 8 - column] != 0) //if not already blanked
                        blankCount++; //increment blank counter
                    tempGrid.InitSetCell(row, 8 - column, 0); //blank opposite cell
                    break;
                case 1: //horizontal symmetry
                    if (tempGrid.Grid[8 - row, column] != 0)
                        blankCount++;
                    tempGrid.InitSetCell(8 - row, column, 0);
                    break;
                case 2: //diagonal symmetry
                    if (tempGrid.Grid[column, row] != 0)
                        blankCount++;
                    tempGrid.InitSetCell(column, row, 0);
                    break;
                default: //diagonal symmetry
                    if (tempGrid.Grid[row, 8 - column] != 0)
                        blankCount++;
                    tempGrid.InitSetCell(column, row, 0);
                    break;
            }
            return tempGrid;
        }

    }
}

剩下的code is in here

答案 1 :(得分:0)

SWIFT 5版本

我拥有的简单代码,在这里

首先,创建函数:

func getNumberSudoku() -> [[Int]] {
    // Original number
    let originalNum = [1,2,3,4,5,6,7,8,9]
    // Create line 1 to 9 and shuffle from original
    let line1 = originalNum.shuffled()
    let line2 = line1.shift(withDistance: 3)
    let line3 = line2.shift(withDistance: 3)
    let line4 = line3.shift(withDistance: 1)
    let line5 = line4.shift(withDistance: 3)
    let line6 = line5.shift(withDistance: 3)
    let line7 = line6.shift(withDistance: 1)
    let line8 = line7.shift(withDistance: 3)
    let line9 = line8.shift(withDistance: 3)
    // Final array
    let renewRow = [line1,line2,line3,line4,line5,line6,line7,line8,line9]
    // Pre-shuffle for column
    let colSh1 = [0,1,2].shuffled()
    let colSh2 = [3,4,5].shuffled()
    let colSh3 = [6,7,8].shuffled()
    let rowSh1 = [0,1,2].shuffled()
    let rowSh2 = [3,4,5].shuffled()
    let rowSh3 = [6,7,8].shuffled()
    // Create the let and var
    let colResult = colSh1 + colSh2 + colSh3
    let rowResult = rowSh1 + rowSh2 + rowSh3
    var preCol: [Int] = []
    var finalCol: [[Int]] = []
    var prerow: [Int] = []
    var finalRow: [[Int]] = []
    // Shuffle the columns
    for x in 0...8 {
        preCol.removeAll()
        for i in 0...8 {
            preCol.append(renewRow[x][colResult[i]])
        }
        finalCol.append(preCol)
    }
    // Shuffle the rows
    for x in 0...8 {
        prerow.removeAll()
        for i in 0...8 {
            prerow.append(finalCol[x][rowResult[i]])
        }
        finalRow.append(prerow)
    }
    // Final, create the array into the [[Int]].
    return finalRow
}

然后的用法:

var resultSudoku = [[Int]]
resultSudoku = getNumberSudoku()