public static void Main(string [] args) { var numerBoardTokenizer = new NumberBoardTokenizer(); using (var streamReader = new StreamReader(new FileStream("./question.txt", FileMode.Open))) { using (var streamWriter = new StreamWriter(new FileStream("./answer.txt", FileMode.OpenOrCreate))) { var lines = streamReader.ReadToEnd().Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); for (var index = 0; index < lines.Length; index += 10) { var header = lines[index]; Console.WriteLine(header); streamWriter.WriteLine(header); var generate = numerBoardTokenizer.Generate(string.Join("", lines, index + 1, 9)); var sudokuBoard = new SudokuBoard(); sudokuBoard.Make(generate); streamWriter.WriteLine(sudokuBoard.Answer); sudokuBoard.Solve(); streamWriter.WriteLine(sudokuBoard.Answer); } } } }
static void Main(string[] args) { //Set test Sudoku field var sudokuBoard = new SudokuBoard(); SetTest(sudokuBoard); sudokuBoard.Print(); //Solve sudokuBoard.Solve(); sudokuBoard.Print(); }
private static void CompleteSolve(SudokuBoard board) { Console.WriteLine("Board:"); Output(board); List <SudokuBoard> solutions = board.Solve().ToList(); Console.WriteLine($"All {solutions.Count} solutions:"); int i = 1; foreach (SudokuBoard solution in solutions) { Console.WriteLine("----------------"); Console.WriteLine($"Solution {i++} / {solutions.Count}:"); Output(solution); } }
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); }
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; }