/// <inheritdoc/> public TPuzzle Solve(TPuzzle puzzle, bool randomizeGuesses = false) { if (!_AreValuesUnique(puzzle.AllPossibleValuesSpan)) { throw new ArgumentException( $"{nameof(puzzle.AllPossibleValuesSpan)} must all be unique. Received values: {puzzle.AllPossibleValuesSpan.ToString()}."); } var puzzleCopy = puzzle.DeepCopy(); var graph = ExactCoverGraph.Create(puzzleCopy); foreach (IConstraint?constraint in _constraints) { if (!constraint.TryConstrain(puzzleCopy, graph)) { throw new ArgumentException("Puzzle violates this solver's constraints."); } ; } if (!(randomizeGuesses ? _TrySolveRandomly(new Guesser <TPuzzle>(puzzleCopy, graph), new Random()) : _TrySolve(new Guesser <TPuzzle>(puzzleCopy, graph)))) { throw new ArgumentException("Failed to solve the given puzzle."); } return(puzzleCopy); }
internal Guesser(TPuzzle puzzle, ExactCoverGraph graph) { _puzzle = puzzle; _graph = graph; _setSquares = new Stack <Guess>(puzzle.NumEmptySquares); MaxGuessCount = _graph.AllPossibleValues.Length; }
private Guesser(Guesser <TPuzzle> other) { _puzzle = other._puzzle.DeepCopy(); // Copy graph, focusing only on 'Unknown' possible square values and (therefore) unsatisfied constraints. _graph = other._graph.CopyUnknowns(); _setSquares = new Stack <Guess>(_puzzle.NumEmptySquares); MaxGuessCount = other.MaxGuessCount; }
private Objective(ExactCoverGraph graph, int countToSatisfy) { _graph = graph; _countToSatisfy = countToSatisfy; _allPossibilitiesAreConcrete = true; _atLeastOnePossibilityIsConcrete = false; _state = NodeState.UNKNOWN; _nextObjectiveInGraph = this; _previousObjectiveInGraph = this; }
private ExactCoverGraph(ExactCoverGraph other) { int length = other._possibilities.Length; _possibilities = new Possibility[length][][]; for (int rowIndex = 0; rowIndex < length; ++rowIndex) { _possibilities[rowIndex] = new Possibility[length][]; } _allPossibleValues = other.AllPossibleValues.ToArray(); ValuesToIndices = other.ValuesToIndices; }
/// <summary> /// Creates an exact-cover graph for solving the given puzzle. /// /// This adds <see cref="Possibility"/> objects for all the unset coordinates in a puzzle on /// creation, as well as <see cref="Objective"/> objects that group all the possible values /// for each location. These effectively implements the constraint: "Each square in the /// puzzle must have one and only one value." /// </summary> /// <param name="puzzle">The puzzle to solve.</param> public static ExactCoverGraph Create(IReadOnlyPuzzle puzzle) { var graph = new ExactCoverGraph(puzzle); int size = puzzle.Size; int numValues = graph._allPossibleValues.Length; for (int rowIndex = 0; rowIndex < size; rowIndex++) { var possibilitiesRow = graph._possibilities[rowIndex]; for (int columnIndex = 0; columnIndex < size; columnIndex++) { var coord = new Coordinate(rowIndex, columnIndex); if (!puzzle[in coord].HasValue)
/// <inheritdoc /> void IObjective.DeselectPossibility(Link toDeselect) { Debug.Assert(!_toPossibility !.GetLinksOnObjective().Contains(toDeselect) || _toPossibility.GetLinksOnObjective().Count() == 1, "Tried to deselect a link that's not connected to this objective."); _ReinsertPossibility(toDeselect); if (_selectedCount == _countToSatisfy) { _state = NodeState.UNKNOWN; ExactCoverGraph.ReattachObjective(this); Links.RevertOthersOnObjective( toDeselect, toReattach => toReattach.Possibility.ReturnFromObjective(toReattach)); } --_selectedCount; }
private SolveStats _ComputeStats(TPuzzle puzzle, bool validateUniquenessOnly, CancellationToken?token) { if (!_AreValuesUnique(puzzle.AllPossibleValuesSpan)) { return(new SolveStats()); } var puzzleCopy = puzzle.DeepCopy(); var graph = ExactCoverGraph.Create(puzzleCopy); foreach (IConstraint?constraint in _constraints) { if (!constraint.TryConstrain(puzzleCopy, graph)) { return(new SolveStats()); } } return(_TryAllSolutions(new Guesser <TPuzzle>(puzzleCopy, graph), validateUniquenessOnly, token)); }
public static Objective CreateFullyConnected( ExactCoverGraph graph, ReadOnlySpan <IPossibility> possibilities, int countToSatisfy) { if (countToSatisfy < 1 || countToSatisfy > possibilities.Length) { throw new ArgumentException($"{nameof(countToSatisfy)} must be in the inclusive range [1, {nameof(possibilities)}.Length]."); } var objective = new Objective(graph, countToSatisfy); foreach (var possibility in possibilities) { Link.CreateConnectedLink(possibility, objective); } graph.AttachObjective(objective); return(objective); }
/// <inheritdoc/> public bool TrySolve(TPuzzle puzzle, bool randomizeGuesses = false) { if (!_AreValuesUnique(puzzle.AllPossibleValuesSpan)) { return(false); } var graph = ExactCoverGraph.Create(puzzle); foreach (IConstraint?constraint in _constraints) { if (!constraint.TryConstrain(puzzle, graph)) { return(false); } } return(randomizeGuesses ? _TrySolveRandomly(new Guesser <TPuzzle>(puzzle, graph), new Random()) : _TrySolve(new Guesser <TPuzzle>(puzzle, graph))); }