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); }
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); }
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)); } } } }
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); }
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); }
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); }