public void NoSolutionFoundToUnsolvableBoard() { int[,] board = GetUnsolvableBoard(); Solver solver = new Solver(); bool solved = solver.Solve(board); Assert.IsFalse(solved); }
public void SolutionIsValid() { int[,] board = GetBoard(); Solver solver = new Solver(); solver.Solve(board); TestHelper.TestSolutionValid(solver.GetFirstSolution()); }
public void CanSolveSampleBoard() { int[,] board = GetBoard(); Solver solver = new Solver(); bool solved = solver.Solve(board); Assert.IsTrue(solved); }
public void CanSolveEmptyBoard() { Solver solver = new Solver(); int[,] board = new int[9, 9]; bool solved = solver.Solve(board); // there are solutions to an empty board Assert.IsTrue(solved); }
public void EmptyBoardSolutionIsValid() { int[,] board = new int[9, 9]; Solver solver = new Solver(); solver.Solve(board); int[,] firstSolution = solver.GetFirstSolution(); TestHelper.TestSolutionValid(firstSolution); }
public void CanSolveSimpleBoard() { Solver solver = new Solver(); int[,] board = GetSimpleBoardToSolve(); bool solved = solver.Solve(board); // there is a solution to this board Assert.IsTrue(solved); Assert.GreaterOrEqual(solver.SolutionCount, 1); }
public void CanSolveBoard() { Solver solver = new Solver(); int[,] board = GetBoard(); bool solved = solver.Solve(board); // there is a solution to this board Assert.IsTrue(solved); Assert.AreEqual(1, solver.SolutionCount); }
public void SolutionIsCorrect() { Solver solver = new Solver(); int[,] board = GetBoard(); solver.Solve(board); int[,] solution = solver.GetFirstSolution(); TestHelper.TestSolutionValid(solution); TestHelper.BoardsAreSameInFilledCells(solution, GetBoardSolution()); }
public void TimeSolution() { Solver solver = new Solver(); int[,] board = GetBoard(); Stopwatch timer = new Stopwatch(); timer.Start(); solver.Solve(board); timer.Stop(); TimeSpan timeToSolve = timer.Elapsed; Assert.Fail("Solving took " + timeToSolve); }
public string generateString(int holeNumber) // generate n boards... { int NUM_MAX = gridSize * 3 - 6; // number of random generated numbers status = 0; List <int> selected = new List <int>(); Random r = new Random(); bool solved = false; //lasvegas algorithms. do // while bm has a solution { sv = null; do // while newBoard got valid puzzle { int numberSelect = NUM_MAX; selected.Clear(); newBoard = initBoard.Copy(); while (numberSelect > 0) { int inputPosition = r.Next(1, gridSize * gridSize); if (!selected.Contains(inputPosition)) { selected.Add(inputPosition); newBoard.setBoard(inputPosition / gridSize, inputPosition % gridSize, r.Next(1, gridSize)); numberSelect--; } } }while (!newBoard.isValid()); sv = new Solver(newBoard); sv.PresentBoard += PresentBoard; bool DONE = false; var t0 = new System.Threading.Thread(() => { threeseconds(); if (DONE == false) { sv.killSolver(); DONE = true; } }); var t1 = new System.Threading.Thread(() => { solved = sv.solve(2, true); Console.WriteLine("solved in three seconds..."); DONE = true; }); t0.Start(); t1.Start(); while (!DONE) { System.Threading.Thread.Sleep(200); } t0.Abort(); t1.Abort(); }while (!solved); Console.WriteLine("digging hole initiated"); status = 1; // DO NOT PRESENT WHILE THIS REGION Board Digged = sv.solution.Copy(); bool[,] DONOTDIG = new bool[gridSize, gridSize]; //holeNumber = r.Next(54, 73); // 9x9 Normal difficulty (temporal), we should implement difficulty later sv = null; int[] shuffledArr = new int[gridSize * gridSize]; for (int i = 0; i < gridSize * gridSize; i++) { DONOTDIG[i / gridSize, i % gridSize] = false; shuffledArr[i] = i; } // shuffling size array... int n = shuffledArr.Length; while (n > 1) { int k = r.Next(n--); int temp = shuffledArr[n]; shuffledArr[n] = shuffledArr[k]; shuffledArr[k] = temp; } //lets dig holes! int cnt = gridSize * gridSize - 1; while (holeNumber > 0) { if (cnt == 0) { break; } int digRow = shuffledArr[cnt] / gridSize; int digCol = shuffledArr[cnt--] % gridSize; int diggedNumber = Digged.getBoard(digRow, digCol); if (DONOTDIG[digRow, digCol]) { continue; } bool nope = false; holeNumber--; for (int i = 1; i <= gridSize; i++) { if (i == diggedNumber) { continue; } Digged.setBoard(digRow, digCol, i); sv = new Solver(Digged); if (sv.solve(2, true)) { DONOTDIG[digRow, digCol] = true; Digged.setBoard(digRow, digCol, diggedNumber); nope = true; holeNumber++; break; } } if (!nope) { DONOTDIG[digRow, digCol] = true; //Console.WriteLine("digging #{0}: {1}, {2}", holeNumber, digRow, digCol); Digged.setBoard(digRow, digCol, 0); newBoard = Digged.Copy(); } } SolBoard = Digged.Copy(); Console.WriteLine("generating complete"); Console.WriteLine(Digged.ToString()); PresentBoard(this, new PresentBoardArgs(Digged.ToString())); return(Digged.ToString()); }
public static void Hint(Board board) { var solver = new Solver(board); solver.Hint(); }
private void RemoveRandomIntegers(int[,] sudokuArray) { var r = new Random(); var boolArray = new bool[5] { false, false, false, false, false }; var excludedCoordinatesList = new List <int[]>(); switch (difficulty) { case Difficulty.Easy: break; case Difficulty.Normal: boolArray[0] = true; break; case Difficulty.Hard: boolArray[0] = true; boolArray[1] = true; break; case Difficulty.VeryHard: boolArray[0] = true; boolArray[1] = true; boolArray[2] = true; boolArray[3] = true; break; case Difficulty.ExtremelyHard: boolArray[0] = true; boolArray[1] = true; boolArray[2] = true; boolArray[3] = true; boolArray[4] = true; break; } for (int j = 0; j < 5; j++) { var rowsList = RandomizeList(integersList); for (int i = 0; i < rowsList.Count; i++) { var coordinate = new int[2] { r.Next(0, fieldsPerRowAmount), rowsList[i] - 1 }; int n = sudokuArray[coordinate[0], coordinate[1]]; if (n != 0 && !Solver.CheckIfCoordinateListContainsCoordinate(excludedCoordinatesList, coordinate)) { sudokuArray[coordinate[0], coordinate[1]] = 0; if (CheckIfDifficultyMet(difficulty, sudokuArray) == -1) { sudokuArray[coordinate[0], coordinate[1]] = n; excludedCoordinatesList.Add(coordinate); } else { var p = Solver.RateSudokuGrid(this, Solver.CopySudoku(sudokuArray), boolArray[0], boolArray[1], boolArray[2], boolArray[3], boolArray[4]); if (p == 0 || p > difficultyRange[1]) { sudokuArray[coordinate[0], coordinate[1]] = n; excludedCoordinatesList.Add(coordinate); } } } } var columnList = RandomizeList(rowsList); for (int i = 0; i < columnList.Count; i++) { var coordinate = new int[2] { columnList[i] - 1, r.Next(0, fieldsPerRowAmount) }; int n = sudokuArray[coordinate[0], coordinate[1]]; if (n != 0 && !Solver.CheckIfCoordinateListContainsCoordinate(excludedCoordinatesList, coordinate)) { sudokuArray[coordinate[0], coordinate[1]] = 0; if (CheckIfDifficultyMet(difficulty, sudokuArray) == -1) { sudokuArray[coordinate[0], coordinate[1]] = n; excludedCoordinatesList.Add(coordinate); } else { var p = Solver.RateSudokuGrid(this, Solver.CopySudoku(sudokuArray), boolArray[0], boolArray[1], boolArray[2], boolArray[3], boolArray[4]); if (p == 0 || p > difficultyRange[1]) { sudokuArray[coordinate[0], coordinate[1]] = n; excludedCoordinatesList.Add(coordinate); } } } } } var rowList = RandomizeList(integersList); for (int x = 0; x < fieldsPerRowAmount; x++) { int row = rowList[x] - 1; var columnList = RandomizeList(rowList); for (int y = 0; y < fieldsPerRowAmount; y++) { int col = columnList[x] - 1; var coordinate = new int[2] { col, row }; int n = sudokuArray[col, row]; if (n != 0 && !Solver.CheckIfCoordinateListContainsCoordinate(excludedCoordinatesList, coordinate)) { sudokuArray[col, row] = 0; if (CheckIfDifficultyMet(difficulty, sudokuArray) == -1) { sudokuArray[coordinate[0], coordinate[1]] = n; excludedCoordinatesList.Add(coordinate); } else { var p = Solver.RateSudokuGrid(this, Solver.CopySudoku(sudokuArray), boolArray[0], boolArray[1], boolArray[2], boolArray[3], boolArray[4]); if (p == 0 || p > difficultyRange[1]) { sudokuArray[coordinate[0], coordinate[1]] = n; excludedCoordinatesList.Add(coordinate); } else { Console.WriteLine(p); } } } } } }
private void CreateIncompleteSudokuGrid() { int[,] sudokuArray = Solver.CopySudoku(SudokuCompleteArray);; protectedPatternsList = DetermineProtectedPatterns(); if (boxPerRowAmount == 3) { switch (difficulty) { case Difficulty.Easy: difficultyRange[0] = 450; difficultyRange[1] = 530; break; case Difficulty.Normal: if (diagonalRows) { difficultyRange[0] = 560; difficultyRange[1] = 660; } else { difficultyRange[0] = 530; difficultyRange[1] = 575; } break; case Difficulty.Hard: if (diagonalRows) { difficultyRange[0] = 660; difficultyRange[1] = 775; } else { difficultyRange[0] = 575; difficultyRange[1] = 630; } break; case Difficulty.VeryHard: if (diagonalRows) { difficultyRange[0] = 775; difficultyRange[1] = 950; } else { difficultyRange[0] = 630; difficultyRange[1] = 700; } break; case Difficulty.ExtremelyHard: if (diagonalRows) { difficultyRange[0] = 950; difficultyRange[1] = 1500; } else { difficultyRange[0] = 700; difficultyRange[1] = 1500; } break; } } while (true) { RemoveRandomIntegers(sudokuArray); if (CheckIfDifficultyMet(difficulty, sudokuArray) == 1) { var p = Solver.RateSudokuGrid(this, Solver.CopySudoku(sudokuArray)); if (p >= difficultyRange[0] && p <= difficultyRange[1]) { rating = p; break; } } sudokuArray = Solver.CopySudoku(SudokuCompleteArray); } SudokuIncompleteArray = Solver.CopySudoku(sudokuArray); }
private short CheckIfDifficultyMet(Difficulty difficulty, int[,] sudokuArray) { //-1: too hard, //0: too easy, //1: met, var tooEasy = false; int[] numbersPerBox; int[] instancesOfNumbers; int[] numbersPerRow; int[] overallNumbers; switch (difficulty) { case Difficulty.Easy: numbersPerBox = new int[2] { 2, 9 }; instancesOfNumbers = new int[2] { 1, 9 }; numbersPerRow = new int[2] { 1, 9 }; overallNumbers = new int[2] { 30, 40 }; break; case Difficulty.Normal: numbersPerBox = new int[2] { 1, 8 }; instancesOfNumbers = new int[2] { 1, 7 }; numbersPerRow = new int[2] { 1, 7 }; overallNumbers = new int[2] { 25, 30 }; break; case Difficulty.Hard: numbersPerBox = new int[2] { 1, 5 }; instancesOfNumbers = new int[2] { 0, 7 }; numbersPerRow = new int[2] { 0, 5 }; overallNumbers = new int[2] { 20, 25 }; break; case Difficulty.VeryHard: numbersPerBox = new int[2] { 0, 7 }; instancesOfNumbers = new int[2] { 0, 7 }; numbersPerRow = new int[2] { 0, 7 }; overallNumbers = new int[2] { 17, 25 }; break; case Difficulty.ExtremelyHard: numbersPerBox = new int[2] { 0, 9 }; instancesOfNumbers = new int[2] { 0, 9 }; numbersPerRow = new int[2] { 0, 9 }; overallNumbers = new int[2] { 15, 25 }; break; default: numbersPerBox = new int[2] { 2, 9 }; instancesOfNumbers = new int[2] { 1, 9 }; numbersPerRow = new int[2] { 1, 9 }; overallNumbers = new int[2] { 30, 35 }; break; } if (!diagonalRows) { overallNumbers[1] += 3; } int n; if (!CheckIfProtectedPatternsAreSafe(sudokuArray)) { return(-1); } foreach (var i in CreateListOfAllIntegers()) { n = Solver.CountInstancesOfInteger(i, sudokuArray); if (n > instancesOfNumbers[1]) { tooEasy = true; } if (n < instancesOfNumbers[0]) { return(-1); } n = Solver.CountIntegersOfRow(i - 1, sudokuArray); if (n > numbersPerRow[1]) { tooEasy = true; } if (n < numbersPerRow[0]) { return(-1); } n = Solver.CountIntegersOfColumn(i - 1, sudokuArray); if (n > numbersPerRow[1]) { tooEasy = true; } if (n < numbersPerRow[0]) { return(-1); } n = Solver.CountIntegersOfBox(SudokuBoxArrayList[i - 1], sudokuArray); if (n > numbersPerBox[1]) { tooEasy = true; } if (n < numbersPerBox[0]) { return(-1); } } n = Solver.CountIntegersOfGrid(sudokuArray); if (n > overallNumbers[1]) { tooEasy = true; } if (n < overallNumbers[0]) { return(-1); } if (tooEasy) { return(0); } return(1); }
public int[,] GetCopyOfCompleteSudokuArray() { return(Solver.CopySudoku(SudokuCompleteArray)); }
/// <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)); }
public static void Solve(Board board) { var solver = new Solver(board); solver.Solve(); }