private static void Main() { Console.Title = Assembly.GetExecutingAssembly().GetName().Name; Console.CursorVisible = true; Console.WriteLine(ConsoleTextBlocks.ShowWelcome("Welcome to Sudoku Solver!")); Console.WriteLine(); GetAvailableSudokuGrids(); Console.WriteLine(); while(true) { SudokuGrid grid; using (var selectedFile = ConsoleInteractions.ShowListAndSelectItem(USER_ABS_DIR_AVAILABLE_GRIDS, SudokuGridFile.Extension, Console.In, Console.Out)) { grid = SudokuGrid.FromBinary(selectedFile); } Console.WriteLine(); var solvingTask = new Task<bool>(() => { var gridSolver = new SudokuSolvingIterationAssumptionTechnique(grid); gridSolver.Solve(); gridSolver.ApplySolutionToGrid(); return gridSolver.SolutionExists; }); var dottingTaskCTS = new CancellationTokenSource(); var dottingTask = new Task(() => ConsoleTextBlocks.ShowBlinkingDots(dottingTaskCTS.Token, Console.Out)); var outputTask = new Task(() => ShowSolution(grid)); var escapeTask = new Task<bool>(ConsoleInteractions.ShowEscapeQuestion); var checkingCorrectnessTask = new Task<bool>(grid.CheckCorrectness); checkingCorrectnessTask.ContinueWith((fin) => { if (fin.Result) { solvingTask.Start(); } else { Console.WriteLine(ConsoleTextMessages.IncorrectInitialConfiguration + "\n"); escapeTask.Start(); } }, TaskContinuationOptions.OnlyOnRanToCompletion); solvingTask.ContinueWith((fin) => dottingTaskCTS.Cancel(), TaskContinuationOptions.OnlyOnRanToCompletion); dottingTask.ContinueWith((fin) => { if (solvingTask.Result) { outputTask.Start(); } else { Console.WriteLine(ConsoleTextMessages.IncorrectInitialConfiguration + "\n"); escapeTask.Start(); } }, TaskContinuationOptions.OnlyOnRanToCompletion); outputTask.ContinueWith((fin) => escapeTask.Start(), TaskContinuationOptions.OnlyOnRanToCompletion); dottingTask.Start(); checkingCorrectnessTask.Start(); escapeTask.Wait(); if (escapeTask.Result) { break; } } }
private SudokuGridCell[,] MakeAssumptionInCell(ref SudokuGridPosition? minCandidatesCellPos) { if (!minCandidatesCellPos.HasValue) { // TODO: optimize: lookup that kind of cell repetitively when its number of candidates changes (decreases): compare against the minimum value from a variable (where to put it?) minCandidatesCellPos = FindFirstCellWithMinimumCandidates(Grid).Position; } var minCandidatesCell = Grid.Cells[minCandidatesCellPos.Value.Y, minCandidatesCellPos.Value.X]; byte assumedNumber = minCandidatesCell.Candidates.ToArray()[0]; assumptionsGrid = (SudokuGrid)Grid.GetType() .GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, CallingConventions.HasThis, new[] { typeof(SudokuGridConstraints) }, null) .Invoke(new [] { (object)Grid.Constraints }); // BUG: !!! as we use the standard grid here, which was not initially intended to be used anywhere except for decode-and-construct scenarios, by the time we reach this code the assumptionsGrid's cells are not initialized yet -- consider introducing some sort of temporary 'grid' (most probably, a kind of 'cells-only layer'). For that reason one of the ctor's code sections was moved into the Reset() method var assumptionsGridSolver = new SudokuSolvingIterationAssumptionTechnique(assumptionsGrid); // WI: cell-to-cell iteration is a very intensive process -- use cells array cloning instead with targeted minCandidatesCell... altering var minCandidatesCellPosCopy = minCandidatesCellPos.Value; assumptionsGrid.IterateLinesXY((x, y) => { SudokuGridCell currCell; var currPos = new SudokuGridPosition(x, y, false); if (currPos.Equals(minCandidatesCellPosCopy)) { currCell = new SudokuGridCell(assumptionsGrid, currPos, assumedNumber); assumptionsGridSolver.lastFilledPos = currPos; assumptionsGridSolver.currCheckingPos = currPos; assumptionsGridSolver.currCheckingPos.Shift(1, assumptionsGrid.Metrics); } else { currCell = new SudokuGridCell(assumptionsGrid, currPos, Grid.Cells[y, x].Number); if (currCell.Candidates != null) { // just to bring into accord with the latest value from the 'parent' grid currCell.Candidates.IntersectWith(Grid.Cells[y, x].Candidates); } } SudokuGrid.AssignNewCell(assumptionsGrid, currCell); //assumptionsGrid.Cells[y, x] = currCell; }); //List<SudokuGridCell[,]> assumptionGridSolutuons = assumptionGridSolver.SearchSolutions(); //if (assumptionGridSolutuons.Count > 0) // correct assumption was made //{ // return assumptionsGrid.Cells; //} //SudokuGridCell[,] solution; if (assumptionsGridSolver.SearchSolutionDebug()) // correct assumption was made { //return solution; return(assumptionsGrid.Cells); } // incorrect assumption was made (executes on contradiction only) minCandidatesCell.Candidates.Remove(assumedNumber); // if there were only two candidates, with assumedNumber being one of them, then the remaining one is a clue if (minCandidatesCell.IsClue) { minCandidatesCellPos = null; var newPos = new SudokuGridPosition(minCandidatesCell.Position.X, minCandidatesCell.Position.Y, false); lastFilledPos = newPos; currCheckingPos = newPos; currCheckingPos.Shift(1, Grid.Metrics); } return(null); }
private SudokuGridCell[,] MakeAssumptionInCell(ref SudokuGridPosition? minCandidatesCellPos) { if (!minCandidatesCellPos.HasValue) { // TODO: optimize: lookup that kind of cell repetitively when its number of candidates changes (decreases): compare against the minimum value from a variable (where to put it?) minCandidatesCellPos = FindFirstCellWithMinimumCandidates(Grid).Position; } var minCandidatesCell = Grid.Cells[minCandidatesCellPos.Value.Y, minCandidatesCellPos.Value.X]; byte assumedNumber = minCandidatesCell.Candidates.ToArray()[0]; assumptionsGrid = (SudokuGrid)Grid.GetType() .GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, CallingConventions.HasThis, new[] { typeof(SudokuGridConstraints) }, null) .Invoke(new [] { (object)Grid.Constraints }); // BUG: !!! as we use the standard grid here, which was not initially intended to be used anywhere except for decode-and-construct scenarios, by the time we reach this code the assumptionsGrid's cells are not initialized yet -- consider introducing some sort of temporary 'grid' (most probably, a kind of 'cells-only layer'). For that reason one of the ctor's code sections was moved into the Reset() method var assumptionsGridSolver = new SudokuSolvingIterationAssumptionTechnique(assumptionsGrid); // WI: cell-to-cell iteration is a very intensive process -- use cells array cloning instead with targeted minCandidatesCell... altering var minCandidatesCellPosCopy = minCandidatesCellPos.Value; assumptionsGrid.IterateLinesXY((x, y) => { SudokuGridCell currCell; var currPos = new SudokuGridPosition(x, y, false); if (currPos.Equals(minCandidatesCellPosCopy)) { currCell = new SudokuGridCell(assumptionsGrid, currPos, assumedNumber); assumptionsGridSolver.lastFilledPos = currPos; assumptionsGridSolver.currCheckingPos = currPos; assumptionsGridSolver.currCheckingPos.Shift(1, assumptionsGrid.Metrics); } else { currCell = new SudokuGridCell(assumptionsGrid, currPos, Grid.Cells[y, x].Number); if (currCell.Candidates != null) { // just to bring into accord with the latest value from the 'parent' grid currCell.Candidates.IntersectWith(Grid.Cells[y, x].Candidates); } } SudokuGrid.AssignNewCell(assumptionsGrid, currCell); //assumptionsGrid.Cells[y, x] = currCell; }); //List<SudokuGridCell[,]> assumptionGridSolutuons = assumptionGridSolver.SearchSolutions(); //if (assumptionGridSolutuons.Count > 0) // correct assumption was made //{ // return assumptionsGrid.Cells; //} //SudokuGridCell[,] solution; if (assumptionsGridSolver.SearchSolutionDebug()) // correct assumption was made { //return solution; return assumptionsGrid.Cells; } // incorrect assumption was made (executes on contradiction only) minCandidatesCell.Candidates.Remove(assumedNumber); // if there were only two candidates, with assumedNumber being one of them, then the remaining one is a clue if (minCandidatesCell.IsClue) { minCandidatesCellPos = null; var newPos = new SudokuGridPosition(minCandidatesCell.Position.X, minCandidatesCell.Position.Y, false); lastFilledPos = newPos; currCheckingPos = newPos; currCheckingPos.Shift(1, Grid.Metrics); } return null; }