private bool _TrySolve(SquareTracker <TPuzzle> tracker) { var puzzle = tracker.Puzzle; if (puzzle.NumEmptySquares == 0) { return(true); } Coordinate c = tracker.GetBestCoordinateToGuess(); Span <int> possibleValues = stackalloc int[puzzle.Size]; int numPossible = tracker.PopulatePossibleValues(in c, possibleValues); for (int i = 0; i < numPossible; ++i) { int possibleValue = possibleValues[i]; if (tracker.TrySet(in c, possibleValue)) { if (_TrySolve(tracker)) { return(true); } tracker.UnsetLast(); } } return(false); }
/// <summary> /// Constructs a solver for the given square tracker. /// </summary> /// <param name="tracker">A square tracker referencing the puzzle to solve.</param> public PuzzleSolver( IPuzzle puzzle, PossibleValues possibleValues, ISudokuRuleKeeper ruleKeeper, ISudokuHeuristic?heuristic = null) { _tracker = new SquareTracker(puzzle, possibleValues, ruleKeeper, heuristic); }
/// <inheritdoc/> public bool TrySolve(TPuzzle puzzle, bool randomizeGuesses = false) { if (!SquareTracker <TPuzzle> .TryInit(puzzle, _ruleKeeper, _heuristic, out SquareTracker <TPuzzle>?tracker)) { return(false); } return(randomizeGuesses ? _TrySolveRandomly(tracker !, new Random()) : _TrySolve(tracker !)); }
/// <summary> /// Creates a deep copy of this ISquareTracker in its current state. /// </summary> public SquareTracker(SquareTracker existing) { _puzzle = existing._puzzle.DeepCopy(); _possibleValues = new PossibleValues(existing._possibleValues); _ruleKeeper = existing._ruleKeeper.CopyWithNewReferences(_puzzle, _possibleValues); _heuristic = existing._heuristic?.CopyWithNewReferences( _puzzle, _possibleValues, _ruleKeeper.GetRules()); _setCoords = new Stack <Coordinate>(existing._setCoords); _coordsThatUsedHeuristics = new Stack <Coordinate>(existing._coordsThatUsedHeuristics); }
/// <summary> /// Creates a deep copy of this tracker in its current state. /// </summary> internal SquareTracker(SquareTracker <TPuzzle> existing) { _puzzle = existing._puzzle.DeepCopy(); _setCoords = new Stack <Coordinate>(existing._setCoords); if (existing._coordsThatUsedHeuristics is not null) { _coordsThatUsedHeuristics = new Stack <Coordinate>(existing._coordsThatUsedHeuristics !); } _ruleKeeper = existing._ruleKeeper.CopyWithNewReferences(_puzzle); _heuristic = existing._heuristic?.CopyWithNewReferences( _puzzle, _ruleKeeper.GetRules()); }
private SolveStats _ComputeStatsForAllSolutions( TPuzzle puzzle, bool validateUniquenessOnly, CancellationToken?cancellationToken) { cancellationToken?.ThrowIfCancellationRequested(); // Copy the puzzle so that the given puzzle is not modified. if (!SquareTracker <TPuzzle> .TryInit(puzzle.DeepCopy(), _ruleKeeper, _heuristic, out SquareTracker <TPuzzle>?tracker)) { // No solutions. return(new SolveStats()); } return(_TryAllSolutions(tracker !, validateUniquenessOnly, cancellationToken)); }
private bool _TrySolveRandomly(SquareTracker <TPuzzle> tracker, Random random) { if (tracker.Puzzle.NumEmptySquares == 0) { return(true); } Coordinate c = tracker.GetBestCoordinateToGuess(); Span <int> possibleValues = stackalloc int[tracker.Puzzle.Size]; int numPossible = tracker.PopulatePossibleValues(in c, possibleValues); while (numPossible > 0) { int possibleValue = Spans.PopRandom(random, possibleValues[0..numPossible--]);
/// <summary> /// Tries to construct and initialize a tracker for the given puzzle, rules, and heuristics. /// </summary> /// <param name="puzzle">The puzzle to solve.</param> /// <param name="ruleKeeper">The rule-keeper to satisfy.</param> /// <param name="heuristic">An optional heuristic to user.</param> /// <param name="tracker"> /// An <c>out</c> param where the tracker will be created. Set to null if initialization /// fails (i.e. this method returns false). /// </param> /// <returns> /// False if initialization fails, for example if the puzzle violates a rule, else true. /// </returns> internal static bool TryInit( TPuzzle puzzle, IRuleKeeper ruleKeeper, IHeuristic?heuristic, out SquareTracker <TPuzzle>?tracker) { if (!ruleKeeper.TryInit(puzzle) || (!heuristic?.TryInitFor(puzzle) ?? false)) { tracker = null; return(false); } tracker = new SquareTracker <TPuzzle>(puzzle, ruleKeeper, heuristic); return(true); }
private static SolveStats _TryAllSolutionsWithGuess( SquareTracker tracker, Coordinate c, List <int> valuesToGuess) { var solveStats = new SolveStats(); for (int i = 0; i < valuesToGuess.Count - 1; i++) { var trackerCopy = new SquareTracker(tracker); if (trackerCopy.TrySet(in c, valuesToGuess[i])) { SolveStats guessStats = _TryAllSolutions(trackerCopy); solveStats.NumSolutionsFound += guessStats.NumSolutionsFound; solveStats.NumSquaresGuessed += guessStats.NumSquaresGuessed; solveStats.NumTotalGuesses += guessStats.NumTotalGuesses; } } if (tracker.TrySet(in c, valuesToGuess[^ 1]))
private static SolveStats _TryAllSolutions(SquareTracker tracker) { if (tracker.Puzzle.NumEmptySquares == 0) { return(new SolveStats() { NumSolutionsFound = 1, }); } Coordinate c = tracker.GetBestCoordinateToGuess(); List <int>?possibleValues = tracker.GetPossibleValues(in c); if (possibleValues.Count == 1) { if (tracker.TrySet(in c, possibleValues[0])) { return(_TryAllSolutions(tracker)); } return(new SolveStats()); } return(_TryAllSolutionsWithGuess(tracker, c, possibleValues)); }
/// <summary> /// Constructs a solver for a standard Sudoku puzzle that uses a standard heuristic and /// standard rule keeper. Provided for convenience. /// </summary> public PuzzleSolver(Puzzle puzzle) { _tracker = new SquareTracker(puzzle); }