public void VerifyThatEmptyPuzzleIsSolved() { var solved = this.boardBruteForceSolver.Solve(new SudokuBoard()); this.boardReaderInstance.Input = EmptyPuzzleSolution; var boardWithSolution = new SudokuBoard(); this.boardReaderInstance.FillBoard(boardWithSolution); Assert.AreEqual(solved, boardWithSolution); }
public SudokuBoard Clone() { var board = new SudokuBoard(); foreach (var cell in AllCells) { board.Cells[cell.RowIndex, cell.ColumnIndex] = new SudokuCell(board, cell.RowIndex, cell.ColumnIndex) { Number = cell.Number, CalculatedValue = cell.CalculatedValue, GuessValue = cell.GuessValue, CalculatedAfterGuess = cell.CalculatedAfterGuess }; } return board; }
public Vm() { Board = new SudokuBoard(); this.PropertyChanged += (sender, args) => { if (args.PropertyName =="Board") { if(_notNewBoard) return; _solver = new Solver(Board); } }; _solver = new Solver(Board); SaveCommand = new RelayCommand(o => Save()); SaveAsCommand = new RelayCommand(o=> SaveAs()); OpenCommand = new RelayCommand(o => Open()); NewCommand = new RelayCommand(o => New()); NextCommand = new RelayCommand(o=>Next(),o=>!_solver.IsDone); }
private void SolveByBruteForce(SudokuBoard board) { var listOfAttempts = new List <Attempt>(); foreach (var sudokuRow in board.Rows) { foreach (var sudokuRowCell in sudokuRow.Cells.Where(c => !c.IsFilled)) { var column = board.Columns.FirstOrDefault(c => c.ColumnNumber == sudokuRowCell.ColumnNumber); var squareNumber = SudokuBoard.CalculateSquareNumber(sudokuRowCell.RowNumber, sudokuRowCell.ColumnNumber); var square = board.Squares.FirstOrDefault(s => s.SquareNumber == squareNumber); var unusedNumber = 0; for (int i = 1; i <= board.NumbersPerUnit; i++) { if (!sudokuRow.HasNumberBeenUsed(i) && !column.HasNumberBeenUsed(i) && !square.HasNumberBeenUsed(i) && !DoesAttemptExist(sudokuRowCell, i, listOfAttempts)) { unusedNumber = i; } } if (unusedNumber == 0) { continue; } //Try setting the number of the cell to the first unused number listOfAttempts.Add(new Attempt(sudokuRowCell, unusedNumber)); var attemptBoard = board.Clone(); var attemptCell = attemptBoard.GetCell(sudokuRowCell.ColumnNumber, sudokuRowCell.RowNumber); attemptCell.Number = unusedNumber; SolveBoard(attemptBoard); if (attemptBoard.IsSolved) { sudokuRowCell.Number = unusedNumber; SolveBoard(board); return; } } } }
static void Main(string[] args) { int?[,] initialBoard = new int?[, ] { { null, null, null, 2, null, null, null, null, 1 }, { null, null, 3, 8, null, null, null, 9, null }, { 7, null, 4, null, 9, 5, 8, null, null }, { 2, 8, null, null, null, null, null, null, 5 }, { null, null, null, null, null, null, null, null, null }, { 6, null, null, null, null, null, null, 7, 3 }, { null, null, 2, 7, 5, null, 6, null, 9 }, { null, 7, null, null, null, 6, 4, null, null }, { 5, null, null, null, null, 9, null, null, null } }; SudokuBoard board = new SudokuBoard(initialBoard); bool solved = SudokuSolver.Solve(board); Console.WriteLine(board.ToString()); Console.WriteLine($"{(solved ? "SOLVED" : "UNSOLVABLE")}"); }
public SudokuCell(SudokuBoard board, int row, int col) { this.board = board; this.RowIndex = row; this.ColumnIndex = col; }
public SudokuSolve(SudokuBoard Board, CheckSudoku Check) { board = Board; check = Check; }
public Solver(SudokuBoard board) { this.board = board; totalMissingNumbers = CountMissingNumbers(); missingNumbers = totalMissingNumbers; }
static void Main(string[] args) { Cell[,] c = new Cell[9, 9]; for (int x = 0; x < 9; ++x) { for (int y = 0; y < 9; ++y) { c[x, y] = new Cell(); } } //test the block resoning solver /*c[0, 3].solved = 1; * c[1, 1].solved = 1; * c[3, 4].solved = 1; * c[4, 0].solved = 1; * c[5, 8].solved = 1; * c[6, 6].solved = 1; * c[7, 5].solved = 1; * c[8, 2].solved = 1;*/ //test case 1 /*c[0, 0].solved = 5; * c[0, 1].solved = 3; * c[0, 4].solved = 7; * * c[1, 0].solved = 6; * c[1, 3].solved = 1; * c[1, 4].solved = 9; * c[1, 5].solved = 5; * * c[2, 1].solved = 9; * c[2, 2].solved = 8; * c[2, 7].solved = 6; * * c[3, 0].solved = 8; * c[3, 4].solved = 6; * c[3, 8].solved = 3; * * c[4, 0].solved = 4; * c[4, 3].solved = 8; * c[4, 5].solved = 3; * c[4, 8].solved = 1; * * c[5, 0].solved = 7; * c[5, 4].solved = 2; * c[5, 8].solved = 6; * * c[6, 1].solved = 6; * c[6, 6].solved = 2; * c[6, 7].solved = 8; * * c[7, 3].solved = 4; * c[7, 4].solved = 1; * c[7, 5].solved = 9; * c[7, 8].solved = 5; * * c[8, 4].solved = 8; * c[8, 7].solved = 7; * c[8, 8].solved = 9;//*/ //test case 3 not logically solvable /*c[1, 5].solved = 3; * c[1, 7].solved = 8; * c[1, 8].solved = 5; * * c[2, 2].solved = 1; * c[2, 4].solved = 2; * * c[3, 3].solved = 5; * c[3, 5].solved = 7; * * c[4, 2].solved = 4; * c[4, 6].solved = 1; * * c[5, 1].solved = 9; * * c[6, 0].solved = 5; * c[6, 7].solved = 7; * c[6, 8].solved = 3; * * c[7, 2].solved = 2; * c[7, 4].solved = 1; * * c[8, 4].solved = 4; * c[8, 8].solved = 9;//*/ //test case 4 isollate bug when trying to recusivly solve /*c[0, 7].solved = 2; * c[0, 8].solved = 1; * * c[1, 0].solved = 2; * c[1, 1].solved = 4; * c[1, 2].solved = 6; * c[1, 3].solved = 1; * c[1, 4].solved = 7; * c[1, 5].solved = 3; * c[1, 6].solved = 9; * c[1, 7].solved = 8; * c[1, 8].solved = 5; * * c[2, 2].solved = 1; * c[2, 4].solved = 2; * * c[3, 0].solved = 1; * c[3, 2].solved = 8; * c[3, 3].solved = 5; * c[3, 5].solved = 7; * c[3, 6].solved = 6; * * c[4, 2].solved = 4; * c[4, 6].solved = 1; * * c[5, 1].solved = 9; * c[5, 3].solved = 4; * c[5, 5].solved = 1; * * c[6, 0].solved = 5; * c[6, 1].solved = 1; * c[6, 2].solved = 9; * c[6, 6].solved = 4; * c[6, 7].solved = 7; * c[6, 8].solved = 3; * * c[7, 0].solved = 4; * c[7, 2].solved = 2; * c[7, 4].solved = 1; * * c[8, 4].solved = 4; * c[8, 6].solved = 2; * c[8, 7].solved = 1; * c[8, 8].solved = 9;//*/ string[] lines = System.IO.File.ReadAllLines(@"C:\Users\Brandon\Documents\sudokuBoard.txt"); for (int x = 0; x < lines.Count(); ++x) { for (int y = 0; y < lines[x].Length; ++y) { c[x, y].solved = lines[x][y] - '0'; } } SudokuBoard b = new SudokuBoard(c); b.printBoard(); b.solve(); //b.printBoard(); }
public bool solve()//solve the current board { bool solved = false; bool changesMadeToBoard = false; Section[] sectionSolverArray = new Section[3];//contain the three types of section solvers sectionSolverArray[0] = new Row(board); sectionSolverArray[1] = new Column(board); sectionSolverArray[2] = new Block(board); List <int>[] sectionsUnsolved = new List <int> [3];//list that will hold the rows, columns, and blocks to check //intitilize all the lists sectionsUnsolved[0] = new List <int>(); sectionsUnsolved[1] = new List <int>(); sectionsUnsolved[2] = new List <int>(); for (int i = 0; i < 9; ++i)//add the number 0-8 to the list { sectionsUnsolved[0].Add(i); sectionsUnsolved[1].Add(i); sectionsUnsolved[2].Add(i); } while (!solved)//continue till solved { //if experiencing errors try reseting interscting numbers for all cells(code commened out underneath) /* * for (int x = 0; x < 9; ++x)//go though the x of the block * { * for (int y = 0; y < 9; ++y)//go throught the y of the block * { * board[x, y].intersectingNumbers = new bool[9]; * for (int t = 0; t < 9; ++t)//go throught the y of the block * { * board[x, y].intersectingNumbers[t] = false; * } * } * }*/ //check all the boards row, columns, and blocks and updating the intersecting numbers of all cells for (int i = 0; i < 3; ++i) { for (int j = 0; j < sectionsUnsolved[i].Count(); ++j) { if (sectionSolverArray[i].updateIntersection(sectionsUnsolved[i][j])) //if the current row, column or block is solved { sectionsUnsolved[i].RemoveAt(j); //remove it from the list j--; //account for it by moving one position back } } } solved = true; changesMadeToBoard = false; //solve throught elemination for (int x = 0; x < 9; ++x) { for (int y = 0; y < 9; ++y) { if (board[x, y].trySolvingByElimination()) //if position is solved during this round of testing(aka something was changed) { changesMadeToBoard = true; //board was changed } if (board[x, y].solved == 0) //if a position is unsolved { solved = false; //board is not solved } } } //if experiencing errors try reset all intersecting numbers for each cell //check all the boards row, columns, and blocks and updating the intersecting numbers of all cells for (int i = 0; i < 3; ++i) { for (int j = 0; j < sectionsUnsolved[i].Count(); ++j) { if (sectionSolverArray[i].updateIntersection(sectionsUnsolved[i][j])) //if the current row, column or block is solved { sectionsUnsolved[i].RemoveAt(j); //remove it from the list j--; //account for it by moving one position back } } } //solve by reasoning //check to see if each block has only one position availible for a number int counter = 0; int xPos = 0, yPos = 0; for (int block = 0; block < 9; ++block) //go throught all the blocks { for (int i = 0; i < 9; ++i) //i represents the possible number to be checked { for (int x = 0; x < 3; ++x) //go though the x of the block { for (int y = 0; y < 3; ++y) //go throught the y of the block { if (board[(block / 3) * 3 + x, (block % 3) * 3 + y].intersectingNumbers[i] == false && board[(block / 3) * 3 + x, (block % 3) * 3 + y].solved == 0) // && numbersSolved[i] == false) //the number i was already in this block { counter++; //count all the false //record the position xPos = x; yPos = y; } } } if (counter == 1)//if there was only one false { //a test print statment /*Console.Write("solved throught reasoning y" + ((block%3) * 3 + yPos) + ", x" + ((block / 3) * 3 + xPos) + " " + (i + 1) + "\n"); * for (int x = 0; x < 3; ++x) * { * for (int y = 0; y < 3; ++y) * { * if (board[blockX * 3 + x, blockY * 3 + y].solved == 0) * Console.Write(Convert.ToInt32(board[blockX * 3 + x, blockY * 3 + y].solvedNumbers[i]) + " "); * } * } * Console.Write('\n');//*/ changesMadeToBoard = true; board[(block / 3) * 3 + xPos, (block % 3) * 3 + yPos].solved = i + 1; solved = false; } counter = 0; } } if (!changesMadeToBoard && !solved)//changes made to board=false and solved=false (if the board is not changed and not solved) { //Console.Write("board not changed\n/////////////////\n"); //printBoard(); int guess = findBestGuess(); if (guess < 0) { Console.WriteLine(guess); return(false); } //Console.Write("guess position: " + guess + "\n"); List <int> pN = board[(guess / 9), (guess % 9)].getNonIntersectingNumbers();//get the list of all non intersecting numbers of the best guess for (int i = 0; i < pN.Count(); ++i) { Cell[,] b = new Cell[9, 9]; for (int x = 0; x < 9; ++x) //go though the x of the block { for (int y = 0; y < 9; ++y) //go throught the y of the block { b[x, y] = new Cell(board[x, y]); } } //Console.Write("making guess of " + (pN[i] + 1) + " i:" + i + " x,y: " + ((guess / 9)) + ", " + ((guess % 9)) + "\n" + "\n"); b[(guess / 9), (guess % 9)].solved = pN[i] + 1; SudokuBoard s = new SudokuBoard(b); if (s.solve()) //try to solve for this board if it cant then it will return to here(true or false) continueing onto the next permutation { return(true); //will continue to return true untill it reaches the original permutation it started to make guesses at } //reset intersecting numbers } //Console.WriteLine("revert back to a board guess"); return(false); } //reset intersecting numbers } //Console.Write("checking board if solved\n"); bool checkBoardSolved = checkIfSolved(); if (checkBoardSolved) { printBoard(); } //return if true if correctly solved false if not correctly solved return(checkBoardSolved); }
private static void Main() { var sudokuBoard = new SudokuBoard(); var sudokuSolver = SetUpSudokuSolver(sudokuBoard); TryToSolvePuzzle(sudokuSolver, sudokuBoard); }
static void Main(string[] args) { bool quit = false; List <SudokuBoard> sudokuTestBoards = new List <SudokuBoard>(); #region Test boards // Normal #14 SudokuBoard boardOne = new SudokuBoard(9, 9, new int[, ] { { 3, 0, 0, 0, 6, 0, 8, 0, 1 }, { 0, 0, 0, 0, 0, 4, 0, 0, 0 }, { 0, 0, 1, 8, 0, 0, 0, 0, 5 }, { 0, 0, 2, 0, 5, 6, 0, 7, 0 }, { 8, 0, 0, 1, 0, 9, 0, 0, 4 }, { 0, 7, 0, 2, 4, 0, 5, 0, 0 }, { 2, 0, 0, 0, 0, 1, 3, 0, 0 }, { 0, 0, 0, 7, 0, 0, 0, 0, 0 }, { 1, 0, 8, 0, 3, 0, 0, 0, 9 } }, "TestBoard_1"); sudokuTestBoards.Add(boardOne); // Easy #2 SudokuBoard boardTwo = new SudokuBoard(9, 9, new int[, ] { { 3, 0, 1, 8, 4, 5, 6, 0, 9 }, { 0, 0, 0, 0, 6, 0, 0, 0, 0 }, { 5, 0, 0, 3, 0, 9, 0, 0, 2 }, { 1, 0, 9, 0, 0, 0, 3, 0, 5 }, { 6, 3, 0, 0, 0, 0, 0, 9, 1 }, { 8, 0, 7, 0, 0, 0, 2, 0, 4 }, { 2, 0, 0, 5, 0, 4, 0, 0, 3 }, { 0, 0, 0, 0, 1, 0, 0, 0, 0 }, { 4, 0, 3, 2, 9, 7, 8, 0, 6 } }, "TestBoard_2"); sudokuTestBoards.Add(boardTwo); // Easy #3 SudokuBoard boardThree = new SudokuBoard(9, 9, new int[, ] { { 0, 4, 8, 0, 0, 0, 9, 2, 0 }, { 0, 0, 3, 8, 1, 5, 4, 0, 0 }, { 5, 0, 0, 0, 0, 0, 0, 0, 1 }, { 0, 0, 1, 5, 0, 6, 8, 0, 0 }, { 0, 7, 0, 2, 0, 9, 0, 1, 0 }, { 0, 0, 2, 1, 0, 8, 5, 0, 0 }, { 7, 0, 0, 0, 0, 0, 0, 0, 3 }, { 0, 0, 9, 7, 5, 1, 2, 0, 0 }, { 0, 1, 4, 0, 0, 0, 7, 5, 0 } }, "TestBoard_3"); sudokuTestBoards.Add(boardThree); // Normal #10 SudokuBoard boardFour = new SudokuBoard(9, 9, new int[, ] { { 0, 0, 0, 0, 0, 2, 0, 1, 0 }, { 3, 0, 8, 0, 9, 1, 0, 0, 0 }, { 0, 0, 0, 6, 3, 0, 0, 5, 0 }, { 4, 2, 0, 0, 0, 0, 5, 0, 0 }, { 0, 3, 9, 0, 0, 0, 1, 6, 0 }, { 0, 0, 1, 0, 0, 0, 0, 7, 2 }, { 0, 9, 0, 0, 4, 3, 0, 0, 0 }, { 0, 0, 0, 5, 2, 0, 7, 0, 9 }, { 0, 6, 0, 9, 0, 0, 0, 0, 0 } }, "TestBoard_4"); sudokuTestBoards.Add(boardFour); // Hard #2 SudokuBoard boardFive = new SudokuBoard(9, 9, new int[, ] { { 0, 4, 0, 0, 1, 0, 2, 0, 6 }, { 0, 3, 0, 5, 9, 0, 8, 0, 1 }, { 0, 0, 0, 4, 0, 0, 0, 0, 0 }, { 6, 1, 8, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 2, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 1, 6, 3 }, { 0, 0, 0, 0, 0, 5, 0, 0, 0 }, { 1, 0, 6, 0, 7, 9, 0, 8, 0 }, { 7, 0, 4, 0, 6, 0, 0, 3, 0 } }, "TestBoard_5"); sudokuTestBoards.Add(boardFive); // 1. Hackerakademi SudokuBoard boardSix = new SudokuBoard(9, 9, new int[, ] { { 2, 0, 3, 0, 0, 0, 7, 0, 4 }, { 0, 9, 1, 2, 7, 4, 5, 0, 3 }, { 0, 6, 7, 3, 5, 9, 2, 0, 1 }, { 7, 0, 0, 6, 0, 3, 0, 4, 5 }, { 5, 3, 4, 0, 1, 7, 6, 2, 8 }, { 0, 8, 0, 0, 2, 0, 9, 0, 7 }, { 9, 1, 0, 5, 3, 6, 4, 7, 2 }, { 3, 4, 5, 0, 9, 0, 0, 0, 6 }, { 0, 0, 2, 0, 0, 1, 0, 0, 9 } }, "TestBoard_6"); sudokuTestBoards.Add(boardSix); #endregion Stopwatch stopwatch = new Stopwatch(); // Select option string lastMessage = ""; while (!quit) { PrintBoardOptions(sudokuTestBoards, lastMessage); int op = GetOption(); if (op == -2) { lastMessage = "Input MUST be a number!"; } if (op == -1) { quit = true; } else if (op == 0) { // Load new board from user lastMessage = String.Empty; UpdateLastMessage("Loading new board from user"); ClearWorkSpace(); Console.SetCursorPosition(0, 25); SudokuBoard b = GetBoard(9, 9); if (b != null) { sudokuTestBoards.Add(b); lastMessage = "New board loaded"; } else { lastMessage = "Error loading new board"; } } else if (op > 0 && op <= sudokuTestBoards.Count) { // Solve board from list SudokuBoard b = sudokuTestBoards[op - 1]; lastMessage = $"Solving board '{ b.Label }'"; ClearWorkSpace(); // Print board Console.SetCursorPosition(0, 25); Solve(b, stopwatch, b.Label); } else { lastMessage = "Invalid option"; } UpdateLastMessage(lastMessage); } }
public Solver(SudokuBoard board) { Board = board; }
public SudokuSolverTest() { this.boardInstance = new SudokuBoard(); this.boardReaderInstance = new StringBoardReaderStrategy(TestCaseInitial); this.boardBruteForceSolver = new BruteForceSudokuSolver(); }
public Solver(Solver antecedent, SudokuBoard board) { this.antecedent = antecedent; this.Board = board; }
public static bool Solve(SudokuBoard board) { return(Solve(board, 0, 0)); }
public Solver(SudokuBoard board) { this.Board = board; }
private static bool IsNotValidNumber(SudokuBoard board, Position position, Position nextPosition, int number) { return(NextCellCanHoldOnlyCurrentNumber(board, nextPosition, number) && !IsFinalPosition(position) && IsOnTheSameRow(position, nextPosition)); }
public IEnumerable <SudokuBoard> Solve() { SudokuProgress Simplify() { bool valid = _rules.All(rule => rule.CheckValid()); if (!valid) { return(SudokuProgress.FAILED); } return(_rules.Aggregate(SudokuProgress.NO_PROGRESS, (progress, rule) => SudokuTile.CombineSolvedState(progress, rule.Solve()))); } // reset solution foreach (SudokuTile tile in _tiles) { tile.ResetPossibles(); } SudokuProgress simplify = SudokuProgress.PROGRESS; while (simplify == SudokuProgress.PROGRESS) { simplify = Simplify(); } if (simplify == SudokuProgress.FAILED) { yield break; } // Find one of the values with the least number of alternatives, but that still has at least 2 alternatives IEnumerable <SudokuTile> query = from rule in _rules from tile in rule where tile.PossibleCount > 1 orderby tile.PossibleCount ascending select tile; SudokuTile chosen = query.FirstOrDefault(); if (chosen == null) { // The board has been completed, we're done! yield return(this); yield break; } foreach (int value in Enumerable.Range(1, _maxValue)) { // Iterate through all the valid possibles on the chosen square and pick a number for it if (!chosen.IsValuePossible(value)) { continue; } SudokuBoard copy = new SudokuBoard(this); copy[chosen.X, chosen.Y].Fix(value, "Trial and error"); foreach (SudokuBoard innerSolution in copy.Solve()) { yield return(innerSolution); } } yield break; }
public Bitmap SolveSudokuPhoto(Bitmap sudokuPhoto) { Bitmap transformedImage = ImageTransformation.TranformImage(sudokuPhoto); //transformedImage.Save(@"D:\k\trash\transformedImage.jpg"); Bitmap thresholdedImage = ImageTransformation.PerformThresholding(transformedImage); //thresholdedImage.Save(@"D:\k\trash\thresholdedImage.jpg"); //todo del //ImageTransformation.Test(transformedImage).Save(@"D:\k\trash\abc.jpg"); //TODO: Threshold will probably be needed for noisy images //var thresholdFilter = new Threshold(100); //thresholdFilter.ApplyInPlace(image); var invertFilter = new Invert(); var invertedImage = invertFilter.Apply(thresholdedImage); //invertedImage.Save(@"D:\k\trash\invertedInput.jpg"); var blobCounter = new BlobCounter { BackgroundThreshold = Color.FromArgb(255, 70, 70, 70) }; blobCounter.ProcessImage(invertedImage); var invertedImageBlobs = blobCounter.GetObjectsInformation(); var boardBlobCandidates = invertedImageBlobs; var biggestBoardBlobCandidate = boardBlobCandidates.OrderByDescending(b => b.Area).First(); var boardBlob = biggestBoardBlobCandidate; var expectedCellBlobHeight = boardBlob.Rectangle.Height / SudokuBoard.NumberOfBoardCellsInSingleDirection; var expectedCellBlobWidth = boardBlob.Rectangle.Width / SudokuBoard.NumberOfBoardCellsInSingleDirection; var blobCellSizeTolerance = 0.25; blobCounter.ProcessImage(thresholdedImage); //todo thresholdedImage = transformedImage; var cellBlobCandidates = blobCounter.GetObjectsInformation(); var cellBlobs = cellBlobCandidates.Where( b => boardBlob.Rectangle.Contains(b.Rectangle) && IsMatch(b.Rectangle.Width, expectedCellBlobWidth, blobCellSizeTolerance) && IsMatch(b.Rectangle.Height, expectedCellBlobHeight, blobCellSizeTolerance)).ToArray(); if (cellBlobs.Length != SudokuBoard.NumberOfBoardCells) { throw new InvalidOperationException(); } var expectedCellsData = Enumerable.Range(0, SudokuBoard.NumberOfBoardCellsInSingleDirection) .SelectMany( cvi => Enumerable.Range(0, SudokuBoard.NumberOfBoardCellsInSingleDirection) .Select(chi => new { CellHorizontalIndex = chi, CellVerticalIndex = cvi, ExpectedCellCenter = new Point((int)(boardBlob.Rectangle.X + (cvi + 0.5) * expectedCellBlobWidth), boardBlob.Rectangle.Y + (int)((chi + 0.5) * expectedCellBlobHeight)), })).ToArray(); var sudokuBoard = new SudokuBoard(); var digitImages = new List <Bitmap>(); var parsedDigitIndexToCellBlobMap = new Dictionary <int, Blob>(); var lastParsedDigitIndex = 0; foreach (var cellBlob in cellBlobs) { // TODO: There may be more than one blob candidate var digitBlob = invertedImageBlobs.SingleOrDefault(b => cellBlob.Rectangle.Contains(b.Rectangle)); if (digitBlob == null) { continue; } var digitImage = thresholdedImage.Clone(digitBlob.Rectangle, thresholdedImage.PixelFormat); digitImages.Add(digitImage); parsedDigitIndexToCellBlobMap[lastParsedDigitIndex++] = cellBlob; } IReadOnlyCollection <int> parsedDigits; using (var digitParser = new DigitParser()) { parsedDigits = digitParser.ParseDigits(digitImages); } foreach (var digitImage in digitImages) { digitImage.Dispose(); } for (var parsedDigitIndex = 0; parsedDigitIndex < parsedDigits.Count; parsedDigitIndex++) { var digitCellBlob = parsedDigitIndexToCellBlobMap[parsedDigitIndex]; var expectedCellData = expectedCellsData.Single(d => digitCellBlob.Rectangle.Contains(d.ExpectedCellCenter)); var parsedDigit = parsedDigits.ElementAt(parsedDigitIndex); if (!SudokuBoard.ValidNumbers.Contains(parsedDigit)) { throw new InvalidOperationException(); } sudokuBoard[expectedCellData.CellHorizontalIndex, expectedCellData.CellVerticalIndex] = parsedDigit; } var solvedBoard = sudokuBoard.Solve(); if (solvedBoard == null) { return(null); } Debug.Assert(solvedBoard.IsComplete() && solvedBoard.IsValid()); var cellsToPrint = Enumerable.Range(0, SudokuBoard.NumberOfBoardCellsInSingleDirection) .SelectMany( vi => Enumerable.Range(0, SudokuBoard.NumberOfBoardCellsInSingleDirection) .Select(hi => new { HorizontalIndex = hi, VerticalIndex = vi })).Where(c => sudokuBoard[c.HorizontalIndex, c.VerticalIndex] == null).Select( i => { var expectedCellData = expectedCellsData.Single( d => d.CellHorizontalIndex == i.HorizontalIndex && d.CellVerticalIndex == i.VerticalIndex); var cellBlob = cellBlobs.Single( b => b.Rectangle.Contains(expectedCellData.ExpectedCellCenter)); var cellCenter = new Point(cellBlob.Rectangle.X + cellBlob.Rectangle.Width / 2, cellBlob.Rectangle.Y + cellBlob.Rectangle.Height / 2); return(new Cell(i.HorizontalIndex, i.VerticalIndex, cellBlob.Rectangle)); }).ToList(); var solutionPhoto = PrintSolutionToSourceImage(thresholdedImage, cellsToPrint, solvedBoard); return(solutionPhoto); }
private void New() { _fileName = null; Board= new SudokuBoard(); }
private Bitmap PrintSolutionToSourceImage(Bitmap sourceImage, IReadOnlyCollection <Cell> cellsToPrint, SudokuBoard solvedBoard) { var solutionImage = sourceImage.Clone(new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), PixelFormat.Format24bppRgb); using (var solutionImageGraphics = Graphics.FromImage(solutionImage)) { solutionImageGraphics.SmoothingMode = SmoothingMode.AntiAlias; solutionImageGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic; solutionImageGraphics.PixelOffsetMode = PixelOffsetMode.HighQuality; foreach (var cell in cellsToPrint) { var borderRatio = 0.1; var horizontalBorderWidth = (int)(cell.Rectangle.Width * borderRatio); var verticalBorderWidth = (int)(cell.Rectangle.Height * borderRatio); var rectangle = new Rectangle( cell.Rectangle.X + horizontalBorderWidth, cell.Rectangle.Y + verticalBorderWidth, cell.Rectangle.Width - 2 * horizontalBorderWidth, cell.Rectangle.Height - 2 * verticalBorderWidth); var cellValue = solvedBoard[cell.HorizontalIndex, cell.VerticalIndex]; solutionImageGraphics.DrawString(cellValue.ToString(), new Font("Tahoma", rectangle.Height, GraphicsUnit.Pixel), Brushes.DarkRed, rectangle); } } return(solutionImage); }
public SudokuBoard SolveSudoku(SudokuBoard board) { return((IsSolvable(board, StartPosition)) ? board : throw new Exception("This sudoku board cannot be solved")); }
public static SudokuBoard SolveInternal(SudokuBoard sudokuBoard) { if (!sudokuBoard.IsValid()) { return(null); } var cells = Enumerable.Range(0, NumberOfBoardCellsInSingleDirection) .SelectMany( vni => Enumerable.Range(0, NumberOfBoardCellsInSingleDirection) .Select(hni => new Cell(hni, vni, sudokuBoard._numbers))).ToList(); Debug.Assert(cells.Count == NumberOfBoardCellsInSingleDirection * NumberOfBoardCellsInSingleDirection); var rows = Enumerable.Range(0, NumberOfBoardCellsInSingleDirection).Select(rowIndex => { var rowCells = cells.Where(c => c.VerticalIndex == rowIndex).ToList(); return(new SolutionRegion(rowCells)); }).ToList(); // TODO: rows and cols are mismatched, imporve just for readability var columns = Enumerable.Range(0, NumberOfBoardCellsInSingleDirection).Select(columnIndex => { var columnCells = cells.Where(c => c.HorizontalIndex == columnIndex).ToList(); return(new SolutionRegion(columnCells)); }).ToList(); var quadrants = Enumerable.Range(0, NumberOfQuadrantsInOneDirection).SelectMany(quadrantHorizontalIndex => Enumerable.Range(0, NumberOfQuadrantsInOneDirection) .Select( quadrantVerticalIndex => new { QuadrantHorizontalIndex = quadrantHorizontalIndex, QuadrantVerticalIndex = quadrantVerticalIndex })).Select(i => { var quardantCells = cells.Where( c => c.HorizontalIndex >= i.QuadrantHorizontalIndex * NumberOfQuadrantCellsInOneDirection && c.HorizontalIndex < (i.QuadrantHorizontalIndex + 1) * NumberOfQuadrantCellsInOneDirection && c.VerticalIndex >= i.QuadrantVerticalIndex * NumberOfQuadrantCellsInOneDirection && c.VerticalIndex < (i.QuadrantVerticalIndex + 1) * NumberOfQuadrantCellsInOneDirection ).ToList(); return(new SolutionRegion(quardantCells)); }).ToList(); Debug.Assert(new[] { rows, columns, quadrants }.All(s => s.Count == NumberOfBoardCellsInSingleDirection)); var solutionRegions = rows.Concat(columns).Concat(quadrants).ToList(); var cellsToSolutionRegionsMap = cells.ToDictionary(c => c, c => (IReadOnlyCollection <SolutionRegion>)solutionRegions.Where(r => r.Cells.Contains(c)).ToList()); Debug.Assert(cellsToSolutionRegionsMap.Values.All(s => s.Count == 3)); var cellsToProcess = cells.Where(c => !c.Number.HasValue).ToList(); var processedCellsBacktrackingInfos = new Stack <CellBacktractingInfo>(); while (true) { var currentCellToProcess = cellsToProcess.OrderBy(c => GetCellPossibleValues(c, cellsToSolutionRegionsMap[c]).Count) .FirstOrDefault(); if (currentCellToProcess == null) { return(sudokuBoard); } Debug.Assert(!currentCellToProcess.Number.HasValue); var currentCellToProcessPossibleValues = GetCellPossibleValues(currentCellToProcess, cellsToSolutionRegionsMap[currentCellToProcess]); var currentCellToProcessBacktrackingInfo = new CellBacktractingInfo(currentCellToProcess, currentCellToProcessPossibleValues); do { var currentCellToProcessPossibleNumber = currentCellToProcessBacktrackingInfo.GetNextPossibleNumber(); if (!currentCellToProcessPossibleNumber.HasValue) { currentCellToProcessBacktrackingInfo.Cell.Number = null; if (!processedCellsBacktrackingInfos.Any()) { return(null); } currentCellToProcessBacktrackingInfo = processedCellsBacktrackingInfos.Pop(); cellsToProcess.Add(currentCellToProcessBacktrackingInfo.Cell); continue; } currentCellToProcessBacktrackingInfo.Cell.Number = currentCellToProcessPossibleNumber; processedCellsBacktrackingInfos.Push(currentCellToProcessBacktrackingInfo); cellsToProcess.Remove(currentCellToProcessBacktrackingInfo.Cell); break; } while (true); } }
private static bool NextCellCanHoldOnlyCurrentNumber(SudokuBoard board, Position nextAvailablePosition, int num) { return(board.GetAllAvailableNumbersForCell(nextAvailablePosition).Count == 1 && board.GetAllAvailableNumbersForCell(nextAvailablePosition)[0] == num); }
public Solver(Solver antecedent, SudokuBoard board) { _antecedent = antecedent; Board = board; }
public SudokuCell(SudokuBoard board, int row, int col) { _board = board; RowIndex = row; ColumnIndex = col; }