private DiagonalUniquenessRule(DiagonalUniquenessRule existing, IReadOnlyPuzzle puzzle) { _puzzle = puzzle; _unsetBackwardDiag = existing._unsetBackwardDiag; _unsetForwardDiag = existing._unsetForwardDiag; _allUnset = existing._allUnset; }
/// <inheritdoc/> public void Constrain(IReadOnlyPuzzle puzzle, ExactCoverMatrix matrix) { Span <bool> isConstraintSatisfiedAtIndex = stackalloc bool[matrix.AllPossibleValues.Length]; for (int row = 0; row < puzzle.Size; row++) { ReadOnlySpan <Square?> rowSquares = matrix.GetSquaresOnRow(row); isConstraintSatisfiedAtIndex.Clear(); for (int col = 0; col < puzzle.Size; col++) { int?puzzleValue = puzzle[row, col]; if (puzzleValue.HasValue) { isConstraintSatisfiedAtIndex[matrix.ValuesToIndices[puzzleValue.Value]] = true; } } for (int valueIndex = 0; valueIndex < isConstraintSatisfiedAtIndex.Length; valueIndex++) { if (isConstraintSatisfiedAtIndex[valueIndex]) { ConstraintUtil.DropPossibleSquaresForValueIndex(rowSquares, valueIndex, matrix); continue; } ConstraintUtil.AddConstraintHeadersForValueIndex(rowSquares, valueIndex, matrix); } } }
internal static void AssertMagicSquaresSatisfied( IReadOnlyPuzzle puzzle, Box[] boxesToCheck, int expectedSum, bool verifyDiagonals) { foreach (Box box in boxesToCheck) { int boxSize = box.Size; var rowSums = new int[boxSize]; var colSums = new int[boxSize]; var startCoord = box.TopLeft; var endCoord = new Coordinate(startCoord.Row + boxSize, startCoord.Column + boxSize); for (int row = startCoord.Row; row < endCoord.Row; ++row) { for (int col = startCoord.Column; col < endCoord.Column; ++col) { var value = puzzle[row, col].Value; rowSums[row - startCoord.Row] += value; colSums[col - startCoord.Column] += value; } } Assert.All(rowSums, sum => Assert.Equal(expectedSum, sum)); Assert.All(colSums, sum => Assert.Equal(expectedSum, sum)); if (verifyDiagonals) { int diagSum = 0; for (Coordinate coord = startCoord; coord != endCoord; coord = new Coordinate(coord.Row + 1, coord.Column + 1)) { var value = puzzle[in coord].Value;
/// <summary> /// Enforces uniqueness of the values at the given coordinates. /// </summary> /// <remarks> /// This drops any <see cref="PossibleValue"/>s that are no longer possible, and /// adds <see cref="ConstraintHeader"/>s and links to enforce this constraint for the ones /// that are still possible. /// </remarks> /// <param name="puzzle">The puzzle being solved.</param> /// <param name="squareCoordinates"> /// The coordinates that must contain unique values. /// </param> /// <param name="matrix">The exact cover matrix for the current puzzle.</param> /// <exception cref="ArgumentException"> /// Thrown if the puzzle violates uniquness for the given coordinates. /// </exception> public static void ImplementUniquenessConstraintForSquares( IReadOnlyPuzzle puzzle, ReadOnlySpan <Coordinate> squareCoordinates, ExactCoverMatrix matrix) { Span <bool> isConstraintSatisfiedAtIndex = stackalloc bool[matrix.AllPossibleValues.Length]; CheckForSetValues(puzzle, matrix, squareCoordinates, isConstraintSatisfiedAtIndex); var squares = new Square?[squareCoordinates.Length]; for (int i = 0; i < squares.Length; i++) { squares[i] = matrix.GetSquare(in squareCoordinates[i]); } for (int valueIndex = 0; valueIndex < isConstraintSatisfiedAtIndex.Length; valueIndex++) { if (isConstraintSatisfiedAtIndex[valueIndex]) { DropPossibleSquaresForValueIndex(squares, valueIndex, matrix); continue; } AddConstraintHeadersForValueIndex(squares, valueIndex, matrix); } }
/// <inheritdoc/> public virtual bool TryInit(IReadOnlyPuzzle puzzle, BitVector uniquePossibleValues) { int numDimensions = GetNumDimensions(puzzle); if (numDimensions != _dimensions?.Length) { _dimensions = new BitVector[numDimensions]; } _dimensions.AsSpan().Fill(uniquePossibleValues); int size = puzzle.Size; for (int row = 0; row < size; ++row) { for (int col = 0; col < size; ++col) { int dimension = GetDimension(new(row, col)); int?val = puzzle[row, col]; if (!val.HasValue) { continue; } if (!_IsPossible(dimension, val.Value)) { // Puzzle has duplicate value on this dimension. return(false); } _RemovePossible(dimension, val.Value); } } return(true); }
/// <inheritdoc/> public void Constrain(IReadOnlyPuzzle puzzle, ExactCoverMatrix matrix) { for (int box = 0; box < puzzle.Size; box++) { _AppendConstraintHeadersInBox(box, (IReadOnlyBoxPuzzle)puzzle, matrix); } }
public static bool TryImplementUniquenessConstraintForSquares( IReadOnlyPuzzle puzzle, ReadOnlySpan <Coordinate> squareCoordinates, ExactCoverGraph graph) { Span <bool> isConstraintSatisfiedAtIndex = stackalloc bool[graph.AllPossibleValues.Length]; if (!TryCheckForSetValues(puzzle, graph, squareCoordinates, isConstraintSatisfiedAtIndex)) { return(false); } Possibility?[]?[] squares = new Possibility[squareCoordinates.Length][]; for (int i = 0; i < squares.Length; i++) { squares[i] = graph.GetAllPossibilitiesAt(in squareCoordinates[i]); } for (int possibilityIndex = 0; possibilityIndex < isConstraintSatisfiedAtIndex.Length; possibilityIndex++) { if (isConstraintSatisfiedAtIndex[possibilityIndex]) { if (!TryDropPossibilitiesAtIndex(squares, possibilityIndex)) { return(false); } continue; } if (!TryAddObjectiveForPossibilityIndex(squares, possibilityIndex, graph, requiredCount: 1, objective: out _)) { return(false); } } return(true); }
public DiagonalUniquenessRule(IReadOnlyPuzzle puzzle, BitVector allUniqueValues) { Debug.Assert(puzzle.Size == allUniqueValues.Count, $"Can't enforce box uniqueness for mismatched puzzle size {puzzle.Size} and number of unique values {allUniqueValues.Count}"); _puzzle = puzzle; _allUnset = _unsetForwardDiag = _unsetBackwardDiag = allUniqueValues; // Iterate through the backward diagonal (like a backslash '\') for (int row = 0, col = 0; row < puzzle.Size; row++, col++) { int?val = puzzle[row, col]; if (val.HasValue) { if (!_unsetBackwardDiag.IsBitSet(val.Value)) { throw new ArgumentException( $"Puzzle does not satisfy diagonal uniqueness rule at ({row}, {col})."); } _unsetBackwardDiag.UnsetBit(val.Value); } } // Iterate through the forward diagonal (like a forward slash '/') for (int row = 0, col = puzzle.Size - 1; row < puzzle.Size; row++, col--) { int?val = puzzle[row, col]; if (val.HasValue) { if (!_unsetForwardDiag.IsBitSet(val.Value)) { throw new ArgumentException( $"Puzzle does not satisfy diagonal uniqueness rule at ({row}, {col})."); } _unsetForwardDiag.UnsetBit(val.Value); } } }
public UniqueInRowHeuristic(IReadOnlyPuzzle puzzle, PossibleValues possibleValues, IMissingRowValuesTracker rule) { _puzzle = puzzle; _possibleValues = possibleValues; _rowTracker = rule; _possiblesToCheckInRow = new BitVector[puzzle.Size]; _previousPossiblesStack = new Stack <IReadOnlyDictionary <Coordinate, BitVector> >(); }
public BoxPossibleValues CopyWithNewReference(IReadOnlyPuzzle puzzle) { if (_puzzle.Size != puzzle.Size) { throw new ArgumentException("Puzzle sizes should match."); } return(new BoxPossibleValues(this, puzzle)); }
/// <inheritdoc/> public ISudokuRule CopyWithNewReference(IReadOnlyPuzzle puzzle) { if (puzzle is IReadOnlyBoxPuzzle boxPuzzle) { return(new BoxUniquenessRule(this, boxPuzzle)); } throw new ArgumentException($"An {nameof(IReadOnlyBoxPuzzle)} is required to copy {nameof(BoxUniquenessRule)}."); }
public UniqueInColumnHeuristic(IReadOnlyPuzzle puzzle, PossibleValues possibleValues, IMissingColumnValuesTracker rule) { _puzzle = puzzle; _possibleValues = possibleValues; _columnTracker = rule; _possiblesToCheckInColumn = new BitVector[puzzle.Size]; _previousPossiblesStack = new Stack <IReadOnlyDictionary <Coordinate, BitVector> >(puzzle.NumEmptySquares); }
/// <summary> /// Creates a deep copy of this heuristic. Requires <c>rules</c> to contain an /// <see cref="IMissingRowValuesTracker"/>. /// </summary> public ISudokuHeuristic CopyWithNewReferences( IReadOnlyPuzzle puzzle, PossibleValues possibleValues, IReadOnlyList <ISudokuRule> rules) { return(new UniqueInRowHeuristic( this, puzzle, possibleValues, (IMissingRowValuesTracker)rules.First(r => r is IMissingRowValuesTracker))); }
public PossibleValues(IReadOnlyPuzzle puzzle, BitVector allPossible) { _possibleValues = new BitVector[puzzle.Size, puzzle.Size]; AllPossible = allPossible; foreach (Coordinate c in puzzle.GetUnsetCoords()) { _possibleValues[c.Row, c.Column] = AllPossible; } }
/// <inheritdoc/> public override bool TryInit(IReadOnlyPuzzle puzzle, BitVector uniquePossibleValues) { if (!base.TryInit(puzzle, uniquePossibleValues)) { return(false); } _puzzle = puzzle; return(true); }
/// <inheritdoc/> public ISudokuRuleKeeper CopyWithNewReferences( IReadOnlyPuzzle puzzle, PossibleValues possibleValues) { if (puzzle is IReadOnlyBoxPuzzle boxPuzzle) { return(new StandardRuleKeeper(this, boxPuzzle, possibleValues)); } throw new ArgumentException($"An {nameof(IReadOnlyBoxPuzzle)} is required to copy {nameof(StandardRuleKeeper)}."); }
private bool _TryConstrainBox(Box box, IReadOnlyPuzzle puzzle, ExactCoverGraph graph) { Coordinate startCoord = box.TopLeft; Span <Coordinate> toConstrain = stackalloc Coordinate[_squareSize]; List <OptionalObjective> setsToOr = new(); for (int rowIdx = 0; rowIdx < _squareSize; ++rowIdx) { for (int i = 0; i < _squareSize; ++i) { toConstrain[i] = new Coordinate(startCoord.Row + rowIdx, startCoord.Column + i); } if (!_TryConstrainToPossibleSets(toConstrain, puzzle, graph, setsToOr)) { return(false); } _ConstrainAndClearOverlappingSets(graph, setsToOr); } for (int colIdx = 0; colIdx < _squareSize; ++colIdx) { for (int i = 0; i < _squareSize; ++i) { toConstrain[i] = new Coordinate(startCoord.Row + i, startCoord.Column + colIdx); } if (!_TryConstrainToPossibleSets(toConstrain, puzzle, graph, setsToOr)) { return(false); } _ConstrainAndClearOverlappingSets(graph, setsToOr); } if (!_includeDiagonals) { return(true); } int lastColumn = startCoord.Column + _squareSize - 1; for (int offset = 0; offset < _squareSize; ++offset) { toConstrain[offset] = new Coordinate(startCoord.Row + offset, lastColumn - offset); } if (!_TryConstrainToPossibleSets(toConstrain, puzzle, graph, setsToOr)) { return(false); } _ConstrainAndClearOverlappingSets(graph, setsToOr); for (int offset = 0; offset < _squareSize; ++offset) { toConstrain[offset] = new Coordinate(startCoord.Row + offset, startCoord.Column + offset); } if (!_TryConstrainToPossibleSets(toConstrain, puzzle, graph, setsToOr)) { return(false); } _ConstrainAndClearOverlappingSets(graph, setsToOr); return(true); }
private static void _ConstrainBackwardDiagonal(IReadOnlyPuzzle puzzle, ExactCoverMatrix matrix) { Span <Coordinate> Coordinates = stackalloc Coordinate[puzzle.Size]; for (int row = 0, col = 0; row < puzzle.Size; row++, col++) { Coordinates[row] = new Coordinate(row, col); } ConstraintUtil.ImplementUniquenessConstraintForSquares(puzzle, Coordinates, matrix); }
public static bool TryCheckForSetValues( IReadOnlyPuzzle puzzle, ExactCoverGraph graph, ReadOnlySpan <Coordinate> squareCoordinates, Span <bool> isValueIndexPresentInSquares) { isValueIndexPresentInSquares.Clear(); foreach (Coordinate coordinate in squareCoordinates) { int?square = puzzle[in coordinate];
private static bool _TryConstrainBackwardDiagonal(IReadOnlyPuzzle puzzle, ExactCoverGraph graph) { Span <Coordinate> Coordinates = stackalloc Coordinate[puzzle.Size]; for (int row = 0, col = 0; row < puzzle.Size; row++, col++) { Coordinates[row] = new Coordinate(row, col); } return(ConstraintUtil.TryImplementUniquenessConstraintForSquares(puzzle, Coordinates, graph)); }
/// <inheritdoc/> public override bool TryInit(IReadOnlyPuzzle puzzle, BitVector uniquePossibleValues) { _boxSize = Boxes.IntSquareRoot(puzzle.Size); _puzzle = puzzle; if (!base.TryInit(puzzle, uniquePossibleValues)) { _puzzle = null; return(false); } return(true); }
public PossibleValues(IReadOnlyPuzzle puzzle) { _possibleValues = new BitVector[puzzle.Size, puzzle.Size]; var allPossible = BitVector.CreateWithSize(puzzle.Size + 1); allPossible.UnsetBit(0); AllPossible = allPossible; foreach (Coordinate c in puzzle.GetUnsetCoords()) { _possibleValues[c.Row, c.Column] = AllPossible; } }
internal static void AssertMagicSquaresSatisfied( IReadOnlyPuzzle puzzle, int expectedSum, bool verifyDiagonals) { int boxSize = Boxes.IntSquareRoot(puzzle.Size); var boxes = new Box[puzzle.Size]; for (int boxIdx = 0; boxIdx < boxes.Length; ++boxIdx) { boxes[boxIdx] = new Box(Boxes.GetStartingBoxCoordinate(boxIdx, boxSize), boxSize); } AssertMagicSquaresSatisfied(puzzle, boxes, expectedSum, verifyDiagonals); }
/// <inheritdoc/> public void Constrain(IReadOnlyPuzzle puzzle, ExactCoverMatrix matrix) { Span <Coordinate> columnCoordinates = stackalloc Coordinate[puzzle.Size]; for (int column = 0; column < puzzle.Size; column++) { for (int row = 0; row < puzzle.Size; row++) { columnCoordinates[row] = new Coordinate(row, column); } ConstraintUtil.ImplementUniquenessConstraintForSquares(puzzle, columnCoordinates, matrix); } }
private UniqueInRowHeuristic( UniqueInRowHeuristic existing, IReadOnlyPuzzle puzzle, PossibleValues possibleValues, IMissingRowValuesTracker rule) { _puzzle = puzzle; _possibleValues = possibleValues; _rowTracker = rule; _possiblesToCheckInRow = (BitVector[])existing._possiblesToCheckInRow.Clone(); _previousPossiblesStack = new Stack <IReadOnlyDictionary <Coordinate, BitVector> >( existing._previousPossiblesStack); }
/// <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)
private bool _TryConstrainToPossibleSets( ReadOnlySpan <Coordinate> toConstrain, IReadOnlyPuzzle puzzle, ExactCoverGraph graph, List <OptionalObjective> setsToOr) { Possibility?[]?[] unsetSquares = new Possibility[toConstrain.Length][]; BitVector alreadySet = new BitVector(); int numUnset = 0; for (int i = 0; i < toConstrain.Length; ++i) { var square = puzzle[in toConstrain[i]];
public DynamicRuleKeeper(IReadOnlyPuzzle puzzle, PossibleValues possibleValues, IReadOnlyList <ISudokuRule> rules) { _puzzle = puzzle; _possibleValues = possibleValues; _coordTracker = new CoordinateTracker(puzzle.Size); _rules = rules; foreach (Coordinate c in _puzzle.GetUnsetCoords()) { foreach (ISudokuRule?r in _rules) { _possibleValues.Intersect(in c, r.GetPossibleValues(in c)); } if (_possibleValues[in c].IsEmpty())
private StandardHeuristic( StandardHeuristic existing, IReadOnlyPuzzle puzzle, PossibleValues possibleValues, IReadOnlyList <ISudokuRule> rules) { _rowHeuristic = (UniqueInRowHeuristic)existing._rowHeuristic.CopyWithNewReferences( puzzle, possibleValues, rules); _columnHeuristic = (UniqueInColumnHeuristic)existing._columnHeuristic .CopyWithNewReferences(puzzle, possibleValues, rules); _boxHeuristic = (UniqueInBoxHeuristic)existing._boxHeuristic.CopyWithNewReferences( puzzle, possibleValues, rules); _numHeuristicsRan = new Stack <int>(existing._numHeuristicsRan); }
/// <inheritdoc /> public bool TryConstrain(IReadOnlyPuzzle puzzle, ExactCoverGraph graph) { if (!_IsCompatible(puzzle)) { return(false); } foreach (Box box in _magicSquares) { if (!_TryConstrainBox(box, puzzle, graph)) { return(false); } } return(true); }