public bool HasSudokuUniqueSolution(SudokuPuzzle sudoku)
        {
            // sudokus with 16 or less determinded fields cannot have a unique solution
            // source: https://www.technologyreview.com/s/426554/mathematicians-solve-minimum-sudoku-problem/
            bool ret = (sudoku.GetSetFields()?.Count > 16);

            if (ret)
            {
                // solve sudoku (gets one solution out of possibly many solutions)
                var solution = SolveSudoku(sudoku);
                ret = (solution != null);

                if (ret)
                {
                    var temp = (SudokuPuzzle)sudoku.Clone();

                    // go through all empty fields (filled out fields are ignored)
                    while (ret && !temp.IsSolved())
                    {
                        // choose a free field with a minimum amount of remaining possibilities
                        int minPossibleValuesCount = temp.GetFreeFields().Select(x => x.GetPossibleValuesCount()).Min();
                        var field = temp.GetFreeFields().Where(x => x.GetPossibleValuesCount() == minPossibleValuesCount).ChooseRandom();

                        // check if the field is determined; if not, there is a second solution => return false
                        ret = isFieldDetermined(temp, solution, field);

                        // apply the determined value to the sudoku => less possibilities
                        field.SetValue(solution.Fields[field.RowIndex, field.ColumnIndex].Value);
                    }
                }
            }

            return(ret);
        }
        public SudokuPuzzle GenerateSudoku(SudokuDifficuty difficulty, int length = 9)
        {
            // get randomly filled out sudoku
            SudokuPuzzle solution = SolveSudoku(new SudokuPuzzle());
            SudokuPuzzle sudoku   = (SudokuPuzzle)solution.Clone();

            // check how many fields need to be removed (depending on desired difficulty)
            int fieldsToRemove = ((length * length) - (int)difficulty);

            while (fieldsToRemove > 0)
            {
                int freeFieldsCount = sudoku.GetFreeFields().Count;

                foreach (var field in sudoku.GetSetFields().Shuffle())
                {
                    if (fieldsToRemove > 0 && isFieldDetermined(sudoku, solution, field.RowIndex, field.ColumnIndex))
                    {
                        field.SetValue(0);
                        fieldsToRemove--;
                    }
                }

                // check if progress was made (if not => try with another sudoku puzzle)
                if ((freeFieldsCount - sudoku.GetFreeFields().Count) == 0)
                {
                    sudoku = GenerateSudoku(difficulty, length);
                    break;
                }
            }

            // check if the generated sudoku is unique. if not create a new one.
            return((HasSudokuUniqueSolution(sudoku)) ? sudoku : GenerateSudoku(difficulty, length));
        }
        protected SudokuPuzzle guessNextField(SudokuPuzzle sudoku, SudokuField field)
        {
            SudokuPuzzle result = null;

            foreach (int value in field.GetPossibleValues().Shuffle())
            {
                // make copy of sudoku and try out possibility
                var copy = (SudokuPuzzle)sudoku.Clone();
                copy.Fields[field.RowIndex, field.ColumnIndex].SetValue(value);

                if (copy.IsValid())
                {
                    // go to next recursion level
                    result = solveSudokuRecursive(copy);

                    // pass correct solution to lower recursion levels
                    if (result != null)
                    {
                        break;
                    }
                }
            }

            return(result);
        }
Exemple #4
0
        private SudokuPuzzle tryNextLevel(SudokuPuzzle sudoku, int row, int column, IEnumerable <int> possibleValues)
        {
            SudokuPuzzle result = null;

            // prepare row / column index for next recursion level
            int nextRow    = row;
            int nextColumn = column;

            incrementFieldIndices(ref nextRow, ref nextColumn, sudoku.Length);

            foreach (int value in possibleValues)
            {
                // make copy of sudoku and try out possibility
                var copy = (SudokuPuzzle)sudoku.Clone();
                copy.Fields[row, column].SetValue(value);

                if (copy.IsValid())
                {
                    // go to next recursion level
                    result = solveSudokuRecursive(copy, nextRow, nextColumn);

                    // pass correct solution to lower recursion levels
                    if (result != null)
                    {
                        break;
                    }
                }
            }

            return(result);
        }
        public void TestRemoveNumbers()
        {
            Assert.ThrowsException <ArgumentNullException>(() => SudokuGenerator.RemoveNumbers(null));
            SudokuPuzzle sudoku = SudokuGenerator.AddNumbers(SudokuPuzzleTest.Size, SudokuPuzzleTest.Difficulty);

            Console.WriteLine(sudoku);
            SudokuPuzzle original = (SudokuPuzzle)sudoku.Clone();

            SudokuGenerator.RemoveNumbers(sudoku);
            Console.WriteLine(sudoku);
            SudokuSolver.RecursiveSolve(sudoku);
            Assert.AreEqual(original, sudoku);
        }
        /// <summary>
        /// Solves the given <see cref="SudokuPuzzle"/>
        /// </summary>
        /// <param name="puzzle">The <see cref="SudokuPuzzle"/> being solved</param>
        /// <returns>A list of <see cref="SudokuPuzzle"/>s that are solutions to the given <see cref="SudokuPuzzle"/></returns>
        public List <SudokuPuzzle> Solve(SudokuPuzzle puzzle)
        {
            List <SudokuPuzzle>  solutions   = new List <SudokuPuzzle>();
            Stack <SudokuPuzzle> puzzleStack = new Stack <SudokuPuzzle>();

            puzzleStack.Push(puzzle.Clone());

            while (puzzleStack.Count > 0)
            {
                try
                {
                    SudokuPuzzle puzzleToSolve = puzzleStack.Pop();
                    if (puzzle.IsValid())
                    {
                        bool puzzleAdvanced = false;
                        do
                        {
                            puzzleAdvanced = false;
                            for (int i = 0; i < this.strategies.Count; ++i)
                            {
                                while (this.strategies[i].AdvancePuzzle(puzzleToSolve))
                                {
                                    puzzleAdvanced = true;
                                }
                            }
                        }while (puzzleAdvanced);

                        if (puzzleToSolve.IsSolved() && puzzleToSolve.IsValid())
                        {
                            solutions.Add(puzzleToSolve);
                            if (solutions.Count > 1)
                            {
                                break;
                            }
                        }
                        else
                        {
                            foreach (SudokuPuzzle guess in this.Guess(puzzleToSolve))
                            {
                                puzzleStack.Push(guess);
                            }
                        }
                    }
                }
                catch (Exception)
                {
                }
            }

            return(solutions);
        }
        /// <summary>
        /// Finds a <see cref="Cell"/> with the least possible values (other than solved <see cref="Cell"/>s),
        /// and tries each possible puzzle using those values in that cell
        /// </summary>
        /// <param name="puzzle">The <see cref="SudokuPuzzle"/> being solved</param>
        /// <returns>The puzzle solutions found from guessing</returns>
        private List <SudokuPuzzle> Guess(SudokuPuzzle puzzle)
        {
            if (!puzzle.IsValid())
            {
                return(new List <SudokuPuzzle>());
            }

            Cell cellToGuessOn = null;
            int  x             = -1;
            int  y             = -1;

            for (int i = 0; i < puzzle.Rows.Count; ++i)
            {
                for (int k = 0; k < puzzle.Rows[i].Count; ++k)
                {
                    Cell cell = puzzle.Rows[i][k];
                    if (cell.Count > 1)
                    {
                        if (cellToGuessOn == null)
                        {
                            cellToGuessOn = cell;
                            x             = i;
                            y             = k;
                        }
                        else if (cellToGuessOn.Count > cell.Count)
                        {
                            cellToGuessOn = cell;
                            x             = i;
                            y             = k;
                        }
                    }
                }
            }

            List <SudokuPuzzle> guesses = new List <SudokuPuzzle>();

            if (cellToGuessOn != null)
            {
                foreach (int allowedValue in cellToGuessOn.AllowedValues.Keys)
                {
                    SudokuPuzzle guess = puzzle.Clone();
                    guess.Rows[x][y].RemoveAllExcept(allowedValue);
                    guesses.Add(guess);
                }
            }

            return(guesses);
        }
Exemple #8
0
        private bool removeField(SudokuPuzzle sudoku, SudokuPuzzle solution)
        {
            foreach (SudokuField field in sudoku.GetSetFields().Shuffle())
            {
                // clone sudoku and set the field value to 0
                SudokuPuzzle temp      = (SudokuPuzzle)sudoku.Clone();
                SudokuField  tempField = temp.Fields[field.RowIndex, field.ColumnIndex];
                tempField.SetValue(0);

                if (HasSudokuUniqueSolution(temp))
                {
                    field.SetValue(0);
                    return(true);
                }
            }

            return(false);
        }
        public void TestClone()
        {
            string       inputFilePath = "TestPuzzleInput.txt";
            SudokuFiler  filer         = new SudokuFiler(inputFilePath);
            SudokuPuzzle puzzle        = filer.CreatePuzzle();

            for (int i = 0; i < puzzle.Dimension * puzzle.Dimension; ++i)
            {
                if (puzzle.Rows[0][i].Count == 1)
                {
                    int allowedValue = puzzle.Rows[0][i].AllowedValue;
                    if (allowedValue != 1)
                    {
                        allowedValue = 1;
                    }
                    else
                    {
                        ++allowedValue;
                    }

                    puzzle.Rows[0][i].AddAllowedValue(allowedValue);
                    break;
                }
            }

            SudokuPuzzle clonedPuzzle = puzzle.Clone();

            for (int i = 0; i < puzzle.Dimension * puzzle.Dimension; ++i)
            {
                for (int k = 0; k < puzzle.Dimension * puzzle.Dimension; ++k)
                {
                    Assert.IsTrue(puzzle.Rows[i][k].Count == clonedPuzzle.Rows[i][k].Count);
                    Assert.IsTrue(puzzle.Columns[i][k].Count == clonedPuzzle.Columns[i][k].Count);
                    Assert.IsTrue(puzzle.Blocks[i][k].Count == clonedPuzzle.Blocks[i][k].Count);
                    foreach (int allowedValue in puzzle.Rows[i][k].AllowedValues.Keys)
                    {
                        Assert.IsTrue(clonedPuzzle.Rows[i][k].AllowedValues.ContainsKey(allowedValue));
                    }
                }
            }
        }
Exemple #10
0
        public bool HasSudokuUniqueSolution(SudokuPuzzle sudoku)
        {
            // solve sudoku (gets one solution out of many solutions)
            var  solution = SolveSudoku(sudoku);
            bool ret      = false;

            if (solution != null)
            {
                int         row    = 0;
                int         column = 0;
                SudokuField originalField;
                var         temp = (SudokuPuzzle)sudoku.Clone();

                // go through all empty fields (filled out fields are ignored)
                while ((originalField = getNextFreeField(temp, ref row, ref column)) != null)
                {
                    // get value of field from solution
                    int solutionValue = solution.Fields[row, column].Value;

                    // try to solve the sudoku again, but without the value from the solution
                    var possibleValues = originalField.GetPossibleValues().Except(new int[] { solutionValue }).ToArray();
                    var secondSolution = tryNextLevel(temp, row, column, possibleValues);

                    // if there is a second solution, the sudoku has definitely not a unique solution => leave with false
                    if (secondSolution != null && !solution.Equals(secondSolution))
                    {
                        goto Leave;
                    }

                    // increment row / column index => this avoids checking a field twice
                    incrementFieldIndices(ref row, ref column, sudoku.Length);
                }
            }

            // all empty fields have a unique solution => there is no second solution => leave with true
            ret = true;

Leave:
            return(ret);
        }
Exemple #11
0
        private SudokuPuzzle solveSudokuRecursive(SudokuPuzzle original, int row = 0, int column = 0)
        {
            SudokuPuzzle result = null;
            SudokuField  field;

            // make a copy of overloaded sudoku
            var copy = (SudokuPuzzle)original.Clone();

            // eliminate possibilities in copy / fill out fields with a single remaining possibility
            copy.EliminatePossibilities();

            // go through all empty fields
            while ((field = getNextFreeField(copy, ref row, ref column)) != null)
            {
                var possibleValues = field.GetPossibleValues().Shuffle();

                if (possibleValues.Count > 1)
                {
                    // case with multiple remaining possibilities => try each of them with brute force
                    result = tryNextLevel(copy, row, column, possibleValues);
                    goto Leave;
                }
                else if (possibleValues.Count == 1)
                {
                    // case with a single remaining possibility => set value
                    field.SetValueIfDetermined();
                }
                else
                {
                    result = null;
                    goto Leave;
                }
            }

            result = copy.IsSolved() ? copy : result;

Leave:
            return(result);
        }
Exemple #12
0
        public SudokuPuzzle GenerateSudoku(SudokuDifficuty difficulty, int length = 9)
        {
            // get randomly filled out sudoku
            SudokuPuzzle source = SolveSudoku(new SudokuPuzzle());
            SudokuPuzzle result;

            int i;
            int fieldsToRemove = ((length * length) - (int)difficulty);

            //DateTime genStart = DateTime.Now;
            //Console.WriteLine($"====================================================");

            do
            {
                result = (SudokuPuzzle)source.Clone();
                result.GetFields1D().ChooseRandom().SetValue(0);

                for (i = 0; i < fieldsToRemove; i++)
                {
                    //DateTime start = DateTime.Now;

                    if (!removeField(result, source))
                    {
                        break;
                    }

                    //DateTime end = DateTime.Now;
                    //Console.WriteLine($"iteration { i } took { (end - start).TotalMilliseconds } ms");
                }

                //if (i < fieldsToRemove) { Console.WriteLine($"generator failed"); }
                //else { Console.WriteLine($"generator successful (total time elapsed: { (DateTime.Now - genStart).TotalMilliseconds } ms)"); }
            }while (i < fieldsToRemove);

            //Console.WriteLine($"====================================================");

            return(result);
        }
        protected SudokuPuzzle solveSudokuRecursive(SudokuPuzzle original)
        {
            // make a copy of overloaded sudoku
            var          copy   = (SudokuPuzzle)original.Clone();
            SudokuPuzzle result = copy;

            // eliminate possibilities in copy / fill out fields with a single remaining possibility
            copy.EliminatePossibilities();

            // go through all empty fields
            if (copy.GetFreeFields().Count > 0)
            {
                // choose a free field with a minimum amount of possibilities
                int minPossibleValuesCount = copy.GetFreeFields().Select(x => x.GetPossibleValuesCount()).Min();
                var field = copy.GetFreeFields().Where(x => x.GetPossibleValuesCount() == minPossibleValuesCount).ChooseRandom();

                var possibleValues = field.GetPossibleValues().Shuffle();

                if (possibleValues.Count > 1)
                {
                    // case with multiple remaining possibilities => try each of them with brute force
                    result = guessNextField(copy, field);
                }
                else if (possibleValues.Count == 1)
                {
                    // case with a single remaining possibility => set value
                    field.SetValueIfDetermined();
                }
                else
                {
                    // sudoku has no valid solution => go back to the previous recursion step
                    result = null;
                }
            }

            return(result);
        }