示例#1
0
        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);
        }
示例#2
0
        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);
        }
示例#3
0
        /// <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));
        }
示例#4
0
        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 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());
        }
        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());
        }
        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);
            }
        }
示例#8
0
        internal static Link CreateConnectedLink(PossibleValue possibleSquare, ConstraintHeader header)
        {
            var squareLink = new Link(possibleSquare, header);

            possibleSquare.Attach(squareLink);
            header.Attach(squareLink);
            return(squareLink);
        }
示例#9
0
        public static void AssertPossibleValueIsOnConstraint(
            PossibleValue possibleValue, ConstraintHeader constraint)
        {
            Link link = constraint.FirstLink;

            do
            {
                if (link.PossibleSquare == possibleValue)
                {
                    return;
                }
                link = link.Down;
            } while (link != constraint.FirstLink);
            Assert.True(false, "No matching possible square value found.");
        }
示例#10
0
        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);
        }
示例#11
0
        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);
        }
示例#12
0
        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);
                }
            }
        }
示例#13
0
        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());
        }
示例#14
0
        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());
        }
示例#15
0
        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());
        }
示例#16
0
 private Link(PossibleValue possibleSquare, ConstraintHeader constraint)
 {
     PossibleSquare = possibleSquare;
     Constraint     = constraint;
     Up             = Down = Right = Left = this;
 }