public void Constrain_ReturnsExpectedConstraints() { int size = 4; int[] possibleValues = new int[] { 1, 3, 5, 7 }; var puzzle = new Puzzle(size); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); Assert.Equal(size * possibleValues.Length, matrix.GetUnsatisfiedConstraintHeaders().Count()); ConstraintHeader firstRowConstraint = matrix.GetSquare(new Coordinate(0, 0)).AllPossibleValues[0].FirstLink.Constraint; ConstraintHeader secondRowConstraint = matrix.GetSquare(new Coordinate(1, 0)).AllPossibleValues[0].FirstLink.Constraint; ConstraintHeader thirdRowConstraint = matrix.GetSquare(new Coordinate(2, 0)).AllPossibleValues[0].FirstLink.Constraint; ConstraintHeader fourthRowConstraint = matrix.GetSquare(new Coordinate(3, 0)).AllPossibleValues[0].FirstLink.Constraint; Assert.NotSame(firstRowConstraint, secondRowConstraint); Assert.NotSame(firstRowConstraint, thirdRowConstraint); Assert.NotSame(firstRowConstraint, fourthRowConstraint); Assert.NotSame(secondRowConstraint, thirdRowConstraint); Assert.NotSame(secondRowConstraint, fourthRowConstraint); Assert.NotSame(thirdRowConstraint, fourthRowConstraint); Assert.Same(firstRowConstraint, matrix.GetSquare(new Coordinate(0, 1)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(firstRowConstraint, matrix.GetSquare(new Coordinate(0, 2)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(firstRowConstraint, matrix.GetSquare(new Coordinate(0, 3)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(secondRowConstraint, matrix.GetSquare(new Coordinate(1, 1)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(secondRowConstraint, matrix.GetSquare(new Coordinate(1, 2)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(secondRowConstraint, matrix.GetSquare(new Coordinate(1, 3)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(thirdRowConstraint, matrix.GetSquare(new Coordinate(2, 1)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(thirdRowConstraint, matrix.GetSquare(new Coordinate(2, 2)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(thirdRowConstraint, matrix.GetSquare(new Coordinate(2, 3)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(fourthRowConstraint, matrix.GetSquare(new Coordinate(3, 1)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(fourthRowConstraint, matrix.GetSquare(new Coordinate(3, 2)).AllPossibleValues[0].FirstLink.Constraint); Assert.Same(fourthRowConstraint, matrix.GetSquare(new Coordinate(3, 3)).AllPossibleValues[0].FirstLink.Constraint); }
/// <inheritdoc/> public void Constrain(IReadOnlyPuzzle puzzle, ExactCoverMatrix matrix) { for (int box = 0; box < puzzle.Size; box++) { _AppendConstraintHeadersInBox(box, (IReadOnlyBoxPuzzle)puzzle, matrix); } }
public void TryDrop_LeavesUnchangedOnFailure() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); new ColumnUniquenessConstraint().Constrain(puzzle, matrix); int valueIndex = 0; Assert.True(matrix.GetSquare(new Coordinate(0, 0)).GetPossibleValue(valueIndex).TryDrop()); Assert.True(matrix.GetSquare(new Coordinate(0, 1)).GetPossibleValue(valueIndex).TryDrop()); Assert.True(matrix.GetSquare(new Coordinate(0, 2)).GetPossibleValue(valueIndex).TryDrop()); Square square = matrix.GetSquare(new Coordinate(0, 3)); PossibleValue possibleValue = square.GetPossibleValue(valueIndex); Link linkA = possibleValue.FirstLink; Link linkB = linkA.Right; ConstraintHeader constraintA = linkA.Constraint; ConstraintHeader constraintB = linkB.Constraint; Assert.False(possibleValue.TryDrop()); Assert.Equal(4, square.NumPossibleValues); Assert.Equal(PossibleSquareState.UNKNOWN, possibleValue.State); Assert.Equal(1, constraintA.Count); Assert.Equal(4, constraintB.Count); Assert.Same(linkA, constraintA.FirstLink); Assert.Contains(linkB, constraintB.GetLinks()); }
/// <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); } } }
public void CreateConnectedLink_WithExistingLinks_ConnectsCorrectly() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); var square = new Square(new Coordinate(0, 0), 2); var possibleSquare = new PossibleValue(square, 1); var constraintHeader = new ConstraintHeader(matrix); var firstLink = Link.CreateConnectedLink(possibleSquare, constraintHeader); var link = Link.CreateConnectedLink(possibleSquare, constraintHeader); Assert.Same(firstLink, link.Up); Assert.Same(firstLink, link.Down); Assert.Same(firstLink, link.Right); Assert.Same(firstLink, link.Left); Assert.Same(link, firstLink.Up); Assert.Same(link, firstLink.Down); Assert.Same(link, firstLink.Right); Assert.Same(link, firstLink.Left); Assert.Same(possibleSquare, link.PossibleSquare); Assert.Same(constraintHeader, link.Constraint); Assert.Equal(2, constraintHeader.Count); Assert.Same(firstLink, constraintHeader.FirstLink); Assert.Same(firstLink, possibleSquare.FirstLink); }
public void TrySatisfyConstraint_WithNoOtherChoicesOnConnectedPossible_Fails() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); new ColumnUniquenessConstraint().Constrain(puzzle, matrix); Square square = matrix.GetSquare(new Coordinate(0, 0)); Link lastLink = square.AllPossibleValues[0].FirstLink; ConstraintHeader header = lastLink.Constraint; for (int i = 1; i < square.AllPossibleValues.Length; i++) { Assert.True(square.AllPossibleValues[i].TryDrop()); } Link link = lastLink.Down; Assert.False(link.TrySatisfyConstraint()); Assert.False(header.IsSatisfied); Assert.Equal(1, square.NumPossibleValues); Assert.Same(lastLink.PossibleSquare, square.GetStillPossibleValues().Single()); Assert.Equal(PossibleSquareState.UNKNOWN, lastLink.PossibleSquare.State); }
public void TrySelect_DropsConstraintConnectedSquareValues() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); new ColumnUniquenessConstraint().Constrain(puzzle, matrix); int valueIndex = 0; PossibleValue possibleValue = matrix.GetSquare(new Coordinate(0, 1)).GetPossibleValue(valueIndex); Assert.True(possibleValue.TrySelect()); ConstraintHeader constraintA = possibleValue.FirstLink.Constraint; ConstraintHeader constraintB = possibleValue.FirstLink.Right.Constraint; Assert.Equal(4, constraintA.GetLinks().Count()); Assert.Equal(4, constraintB.GetLinks().Count()); foreach (Link link in constraintA.GetLinks()) { if (link.PossibleSquare == possibleValue) { continue; } Assert.Equal(PossibleSquareState.DROPPED, link.PossibleSquare.State); } foreach (Link link in constraintB.GetLinks()) { if (link.PossibleSquare == possibleValue) { continue; } Assert.Equal(PossibleSquareState.DROPPED, link.PossibleSquare.State); } }
/// <summary> /// Add a <see cref="ConstraintHeader"/> connecting all the /// <see cref="PossibleValue"/>s at the given <paramref name="valueIndex"/> on the /// given <paramref name="squares"/>. Skips null squares, null possible values, and any /// possible values in a known state (i.e. dropped or selected). /// </summary> /// <param name="squares">The squares to add possible square values from.</param> /// <param name="valueIndex"> /// The value index of the possible value within the squares. /// </param> /// <param name="matrix">The matrix for the current puzzle being solved.</param> public static void AddConstraintHeadersForValueIndex( ReadOnlySpan <Square?> squares, int valueIndex, ExactCoverMatrix matrix) { var possibleSquares = new PossibleValue[squares.Length]; int numPossibleSquares = 0; for (int i = 0; i < squares.Length; i++) { Square?square = squares[i]; if (square is null) { continue; } PossibleValue?possibleSquare = square.GetPossibleValue(valueIndex); if (possibleSquare is null || possibleSquare.State != PossibleSquareState.UNKNOWN) { continue; } possibleSquares[numPossibleSquares++] = possibleSquare; } ConstraintHeader.CreateConnectedHeader( matrix, new ReadOnlySpan <PossibleValue>(possibleSquares, 0, numPossibleSquares)); }
/// <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); } }
public void Return_WithSatisfiedConstraint_UndropsTheRowAsExpected() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); new ColumnUniquenessConstraint().Constrain(puzzle, matrix); int valueIndex = 0; Square square = matrix.GetSquare(new Coordinate(0, 0)); PossibleValue possibleValue = square.GetPossibleValue(valueIndex); Link linkA = possibleValue.FirstLink; Link linkB = linkA.Right; ConstraintHeader constraintA = linkA.Constraint; ConstraintHeader constraintB = linkB.Constraint; Assert.True(constraintB.TrySatisfyFrom(possibleValue.FirstLink.Right.Up)); possibleValue.Return(); Assert.Equal(4, square.NumPossibleValues); Assert.Equal(PossibleSquareState.UNKNOWN, possibleValue.State); Assert.Equal(4, constraintA.Count); Assert.Equal(4, constraintB.Count); Assert.Contains(linkA, constraintA.GetLinks()); Assert.Contains(linkB, constraintB.GetLinks()); }
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); }
/// <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); } }
/// <summary> /// Fills the <paramref name="isValueIndexPresentInSquares"/> span according to which /// value indices are already set in the given list of /// <paramref name="squareCoordinates"/>. Each index is true if that value is already set. /// </summary> /// <param name="puzzle">The current puzzle being solved.</param> /// <param name="matrix">The matrix for the puzzle being solved.</param> /// <param name="squareCoordinates">The coordinates to check.</param> /// <param name="isValueIndexPresentInSquares"> /// An array that will be updated to indicate which values are set. /// </param> public static void CheckForSetValues( IReadOnlyPuzzle puzzle, ExactCoverMatrix matrix, ReadOnlySpan <Coordinate> squareCoordinates, Span <bool> isValueIndexPresentInSquares) { isValueIndexPresentInSquares.Fill(false); for (int i = 0; i < squareCoordinates.Length; i++) { int?puzzleValue = puzzle[squareCoordinates[i]]; if (puzzleValue.HasValue) { isValueIndexPresentInSquares[matrix.ValuesToIndices[puzzleValue.Value]] = true; } } }
public void TrySelect_SelectsSquare() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); new ColumnUniquenessConstraint().Constrain(puzzle, matrix); int valueIndex = 0; Square square = matrix.GetSquare(new Coordinate(0, 1)); PossibleValue possibleValue = square.GetPossibleValue(valueIndex); Assert.True(possibleValue.TrySelect()); Assert.Equal(PossibleSquareState.SELECTED, possibleValue.State); }
public void Deselect_WithSelectedValue_SetsStateAndSquareCountCorrectly() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); new ColumnUniquenessConstraint().Constrain(puzzle, matrix); int valueIndex = 0; Square square = matrix.GetSquare(new Coordinate(0, 1)); PossibleValue possibleValue = square.GetPossibleValue(valueIndex); Assert.True(possibleValue.TrySelect()); possibleValue.Deselect(); Assert.Equal(PossibleSquareState.UNKNOWN, possibleValue.State); Assert.Equal(4, square.NumPossibleValues); }
private static void _AppendConstraintHeadersInBox( int box, IReadOnlyBoxPuzzle puzzle, ExactCoverMatrix matrix) { Coordinate startCoord = puzzle.GetStartingBoxCoordinate(box); var endCoord = new Coordinate( startCoord.Row + puzzle.BoxSize, startCoord.Column + puzzle.BoxSize); Span <Coordinate> boxCoordinates = stackalloc Coordinate[puzzle.Size]; int i = 0; for (int row = startCoord.Row; row < endCoord.Row; row++) { for (int col = startCoord.Column; col < endCoord.Column; col++) { boxCoordinates[i++] = new Coordinate(row, col); } } ConstraintUtil.ImplementUniquenessConstraintForSquares(puzzle, boxCoordinates, matrix); }
public void Constrain_SetsUpSquareLinksForAllPossibleValues() { int size = 4; int[] possibleValues = new int[] { 1, 3, 5, 7 }; var puzzle = new Puzzle(size); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); for (int row = 0; row < size; row++) { for (int col = 0; col < size; col++) { foreach (PossibleValue possibleValue in matrix.GetSquare(new Coordinate(row, col)).AllPossibleValues) { Assert.NotNull(possibleValue.FirstLink); } } } }
public void TryRemoveFromConstraint_OnSuccess() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); var constraintHeader = ConstraintHeader.CreateConnectedHeader( matrix, matrix.GetSquaresOnRow(0).ToArray().Select(s => s.AllPossibleValues[0]).ToArray()); Link firstLink = constraintHeader.FirstLink; Link secondLink = firstLink.Down; Link thirdLink = secondLink.Down; Link fourthLink = thirdLink.Down; Assert.True(firstLink.TryRemoveFromConstraint()); Assert.Same(fourthLink, firstLink.Up); Assert.Same(secondLink, firstLink.Down); Assert.Same(secondLink, constraintHeader.FirstLink); Assert.Same(fourthLink, secondLink.Up); Assert.Same(secondLink, fourthLink.Down); Assert.Equal(3, constraintHeader.Count); }
/// <summary> /// Drops <see cref="PossibleValue"/>s with the given <paramref name="valueIndex"/> /// from the given set of <paramref name="squares"/>. Null squares and possible values are /// ignored. /// </summary> /// <param name="squares">The squares to drop, if not null.</param> /// <param name="valueIndex"> /// The value index of the possible values within the squares. /// </param> /// <param name="matrix">The matrix for the puzzle currently being solved.</param> /// <exception cref="ArgumentException"> /// Thrown if the puzzle violates uniquness for the given coordinates. /// </exception> public static void DropPossibleSquaresForValueIndex( ReadOnlySpan <Square?> squares, int valueIndex, ExactCoverMatrix matrix) { for (int i = 0; i < squares.Length; i++) { Square?square = squares[i]; if (square is null) { continue; } PossibleValue?possibleValue = square.GetPossibleValue(valueIndex); if (possibleValue is null) { continue; } if (possibleValue.State != PossibleSquareState.DROPPED && !possibleValue.TryDrop()) { throw new ArgumentException( $"Puzzle violated constraints for value {matrix.AllPossibleValues[valueIndex]} at square {square.Coordinate}."); } } }
public void TryRemoveFromConstraint_WhenConstraintSatisfied_LeavesUnchanged() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); var constraintHeader = ConstraintHeader.CreateConnectedHeader( matrix, matrix.GetSquaresOnRow(0).ToArray().Select(s => s.AllPossibleValues[0]).ToArray()); Link firstLink = constraintHeader.FirstLink; Link secondLink = firstLink.Down; Link thirdLink = secondLink.Down; Link fourthLink = thirdLink.Down; int expectedConstraintCount = constraintHeader.Count; Assert.True(firstLink.TrySatisfyConstraint()); Assert.True(secondLink.TryRemoveFromConstraint()); Assert.Equal(expectedConstraintCount, constraintHeader.Count); Assert.Same(firstLink, secondLink.Up); Assert.Same(secondLink, firstLink.Down); Assert.Same(secondLink, thirdLink.Up); Assert.Same(thirdLink, secondLink.Down); }
public void Deselect_WithSelectedValue_ReturnsDroppedRows() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); new ColumnUniquenessConstraint().Constrain(puzzle, matrix); int valueIndex = 0; Square square = matrix.GetSquare(new Coordinate(0, 1)); PossibleValue possibleValue = square.GetPossibleValue(valueIndex); Assert.True(possibleValue.TrySelect()); possibleValue.Deselect(); Link linkA = possibleValue.FirstLink; Link linkB = linkA.Right; ConstraintHeader constraintA = linkA.Constraint; ConstraintHeader constraintB = linkB.Constraint; Assert.False(constraintA.IsSatisfied); Assert.False(constraintB.IsSatisfied); Assert.Equal(4, constraintA.Count); Assert.Equal(4, constraintB.Count); foreach (Link link in constraintA.GetLinks()) { if (link != linkA) { Assert.Equal(PossibleSquareState.UNKNOWN, link.PossibleSquare.State); } } foreach (Link link in constraintB.GetLinks()) { if (link != linkB) { Assert.Equal(PossibleSquareState.UNKNOWN, link.PossibleSquare.State); } } }
public void TrySelect_SatisfiesConstraints() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); new ColumnUniquenessConstraint().Constrain(puzzle, matrix); int valueIndex = 0; PossibleValue possibleValue = matrix.GetSquare(new Coordinate(0, 1)).GetPossibleValue(valueIndex); Assert.True(possibleValue.TrySelect()); ConstraintHeader constraintA = possibleValue.FirstLink.Constraint; ConstraintHeader constraintB = possibleValue.FirstLink.Right.Constraint; Assert.True(constraintA.IsSatisfied); Assert.True(constraintB.IsSatisfied); Assert.Equal(4, constraintA.Count); Assert.Equal(4, constraintB.Count); Assert.DoesNotContain(constraintA, matrix.GetUnsatisfiedConstraintHeaders()); Assert.DoesNotContain(constraintB, matrix.GetUnsatisfiedConstraintHeaders()); }
public void TrySatisfyConstraint_Succeeds() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); new ColumnUniquenessConstraint().Constrain(puzzle, matrix); Link link = matrix.GetSquare(new Coordinate(0, 0)).AllPossibleValues[0].FirstLink; ConstraintHeader header = link.Constraint; Assert.True(link.TrySatisfyConstraint()); Assert.True(header.IsSatisfied); Assert.True(link.Constraint.IsSatisfied); // Still connected horizontally. Assert.Same(link, link.Right.Left); Assert.Same(link, link.Left.Right); // Vertically connected links are dropped form possible values. Assert.Equal(PossibleSquareState.DROPPED, link.Up.PossibleSquare.State); Assert.Equal(3, link.Up.PossibleSquare.Square.NumPossibleValues); // Still connected vertically. Assert.Contains(link, link.Constraint.GetLinks()); }
public void TryDrop_DropsOnSuccess() { var puzzle = new Puzzle(4); var matrix = new ExactCoverMatrix(puzzle); new RowUniquenessConstraint().Constrain(puzzle, matrix); new ColumnUniquenessConstraint().Constrain(puzzle, matrix); int valueIndex = 1; Square square = matrix.GetSquare(new Coordinate(1, 0)); PossibleValue possibleValue = square.GetPossibleValue(valueIndex); Link linkA = possibleValue.FirstLink; Link linkB = linkA.Right; ConstraintHeader constraintA = linkA.Constraint; ConstraintHeader constraintB = linkB.Constraint; Assert.True(possibleValue.TryDrop()); Assert.Equal(3, square.NumPossibleValues); Assert.Equal(PossibleSquareState.DROPPED, possibleValue.State); Assert.Equal(3, constraintA.Count); Assert.Equal(3, constraintB.Count); Assert.DoesNotContain(linkA, constraintA.GetLinks()); Assert.DoesNotContain(linkB, constraintB.GetLinks()); }
/// <inheritdoc/> public void Constrain(IReadOnlyPuzzle puzzle, ExactCoverMatrix matrix) { _ConstrainForwardDiagonal(puzzle, matrix); _ConstrainBackwardDiagonal(puzzle, matrix); }