public void TrySelectAndDeselectPossibility_WithMultipleRequired_Works() { var puzzle = new Puzzle(4); var matrix = ExactCoverGraph.Create(puzzle); var possibilities = new FakePossibility[] { new FakePossibility(), new FakePossibility(), }; var firstSelected = possibilities[0]; var secondSelected = possibilities[1]; var concreteObjective = Objective.CreateFullyConnected(matrix, possibilities, 2); IObjective objective = concreteObjective; Assert.True(objective.TrySelectPossibility(firstSelected.AttachedObjectives.First())); Assert.NotEqual(NodeState.SELECTED, concreteObjective.State); Assert.Equal(1, concreteObjective.CountUnknown); Assert.Single(objective.GetUnknownDirectPossibilities(), secondSelected); Assert.Contains(concreteObjective, matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities()); Assert.True(objective.TrySelectPossibility(secondSelected.AttachedObjectives.First())); Assert.Equal(NodeState.SELECTED, concreteObjective.State); Assert.Equal(0, concreteObjective.CountUnknown); Assert.True(!matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities().Contains(concreteObjective) || matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities().Count() == 1); objective.DeselectPossibility(secondSelected.AttachedObjectives.First()); Assert.NotEqual(NodeState.SELECTED, concreteObjective.State); Assert.Equal(1, concreteObjective.CountUnknown); Assert.Single(objective.GetUnknownDirectPossibilities(), secondSelected); Assert.Contains(concreteObjective, matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities()); objective.DeselectPossibility(firstSelected.AttachedObjectives.First()); Assert.Equal(2, concreteObjective.CountUnknown); }
public void TrySelectAndDeselectPossibility_WithOneRequired_Works() { var puzzle = new Puzzle(4); var matrix = ExactCoverGraph.Create(puzzle); var possibilities = new FakePossibility[] { new FakePossibility(), new FakePossibility(), }; var selectedPossibility = possibilities[0]; var droppedPossibility = possibilities[1]; var objective = Objective.CreateFullyConnected(matrix, possibilities, 1); Assert.NotEqual(NodeState.SELECTED, objective.State); ((IObjective)objective).TrySelectPossibility(selectedPossibility.AttachedObjectives.First()); Assert.Equal(NodeState.SELECTED, objective.State); Assert.DoesNotContain(objective, matrix.GetUnsatisfiedRequiredObjectives()); Assert.Empty(((IObjective)objective).GetUnknownDirectPossibilities()); Assert.Empty(selectedPossibility.DroppedFromObjectives); Assert.Single(droppedPossibility.DroppedFromObjectives); ((IObjective)objective).DeselectPossibility(selectedPossibility.AttachedObjectives.First()); Assert.NotEqual(NodeState.SELECTED, objective.State); Assert.Contains(objective, matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities()); Assert.Empty(selectedPossibility.DroppedFromObjectives); Assert.Empty(droppedPossibility.DroppedFromObjectives); Assert.Contains(selectedPossibility, ((IObjective)objective).GetUnknownDirectPossibilities()); Assert.Contains(droppedPossibility, ((IObjective)objective).GetUnknownDirectPossibilities()); }
public void TryDropAndReturnPossibility_Succeeds() { var puzzle = new Puzzle(4); var matrix = ExactCoverGraph.Create(puzzle); var possibilities = new FakePossibility[] { new FakePossibility(), new FakePossibility(), new FakePossibility(), }; var toDrop = possibilities[0]; var concreteObjective = Objective.CreateFullyConnected(matrix, possibilities, 2); IObjective objective = concreteObjective; Assert.True(objective.TryDropPossibility(toDrop.AttachedObjectives.First())); Assert.Equal(2, concreteObjective.CountUnknown); Assert.Equal(2, objective.GetUnknownDirectPossibilities().Count()); Assert.Contains(possibilities[1], objective.GetUnknownDirectPossibilities()); Assert.Contains(possibilities[2], objective.GetUnknownDirectPossibilities()); Assert.Empty(toDrop.DroppedFromObjectives); objective.ReturnPossibility(toDrop.AttachedObjectives.First()); Assert.Equal(3, concreteObjective.CountUnknown); Assert.Equal(3, objective.GetUnknownDirectPossibilities().Count()); Assert.Contains(toDrop, objective.GetUnknownDirectPossibilities()); Assert.Contains(possibilities[1], objective.GetUnknownDirectPossibilities()); Assert.Contains(possibilities[2], objective.GetUnknownDirectPossibilities()); }
public void Create_ConfiguresSquareObjectives() { var puzzle = new Puzzle(4); var graph = ExactCoverGraph.Create(puzzle); var objectives = graph.GetUnsatisfiedRequiredObjectives(); Assert.Equal(puzzle.Size * puzzle.Size, objectives.Count()); var seenCoordinates = new HashSet <Coordinate>(); var possibilityIndices = new HashSet <int>() { 0, 1, 2, 3 }; Assert.All(objectives, concreteObjective => { IObjective objective = concreteObjective; var possibilities = objective.GetUnknownDirectPossibilities().Cast <Possibility>().ToArray(); // Assert that each square links every possibility at that coordinate. Assert.Equal(puzzle.Size, possibilities.Length); Assert.Equal(possibilityIndices, new HashSet <int>(possibilities.Select(p => p.Index))); var firstCoord = possibilities.First().Coordinate; Assert.All(possibilities, p => { Assert.Equal(firstCoord, p.Coordinate); Assert.Equal(NodeState.UNKNOWN, p.State); }); // Assert an objective is made for each square. Assert.DoesNotContain(firstCoord, seenCoordinates); seenCoordinates.Add(firstCoord); }); }
public void CreateFullyConnected_ConnectsCorrectly(int numPossibilities, int numRequired, bool asConcrete) { int size = 4; var puzzle = new Puzzle(size); var matrix = ExactCoverGraph.Create(puzzle); var possibilities = Possibilities.CreatePossibilities(new Coordinate(), numPossibilities); Objective objective; if (asConcrete) { objective = Objective.CreateFullyConnected(matrix, new ReadOnlySpan <Possibility>(possibilities), numRequired); } else { objective = Objective.CreateFullyConnected(matrix, new ReadOnlySpan <IPossibility>(possibilities), numRequired); } Assert.True(objective.AllUnknownPossibilitiesAreConcrete); Assert.True(((IObjective)objective).IsRequired); Assert.Equal(numPossibilities == numRequired, objective.AllUnknownPossibilitiesAreRequired); Assert.NotEqual(NodeState.SELECTED, objective.State); Assert.Equal(possibilities.Length, objective.CountUnknown); Assert.Equal(numRequired, objective.TotalCountToSatisfy); Assert.All(possibilities, p => Assert.Contains(p, ((IObjective)objective).GetUnknownDirectPossibilities())); Assert.All(((IObjective)objective).GetUnknownDirectPossibilities(), p => Assert.Contains(p, possibilities)); Assert.Contains(objective, matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities()); }
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 void TryConstrain_ConstrainsCorrectly() { var puzzle = new Puzzle(new int?[][] { new int?[] { null, null, 9, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, 3, 5, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, }); var boxesToConstrain = new Box[] { new Box(new Coordinate(0, 0), 3), }; var constraint = new MagicSquaresConstraint( _CreateStandardPossibleValues(9), boxesToConstrain, includeDiagonals: false); var graph = ExactCoverGraph.Create(puzzle); Assert.True(constraint.TryConstrain(puzzle, graph)); _AssertPossibleValuesAtSquare(new(0, 0), new int[] { 1, 2, 4, 5 }, graph); _AssertPossibleValuesAtSquare(new(0, 1), new int[] { 4, 5 }, graph); _AssertPossibleValuesAtSquare(new(1, 0), new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, graph); _AssertPossibleValuesAtSquare(new(1, 1), new int[] { 4, 5, 7, 8 }, graph); _AssertPossibleValuesAtSquare(new(1, 2), new int[] { 1 }, graph); _AssertPossibleValuesAtSquare(new(2, 0), new int[] { 7 }, graph); _AssertPossibleValuesAtSquare(new(3, 0), new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, graph); }
public void TryConstrain_WithUniqueValuesInDiagonals_Succeeds() { var puzzle = new Puzzle(new int?[][] { new int?[] { 1, null, null, null }, new int?[] { null, null, 2, null }, new int?[] { null, 3, 3, null }, new int?[] { 4, null, null, null }, }); var graph = ExactCoverGraph.Create(puzzle); var constraint = new DiagonalUniquenessConstraint(); Assert.True(constraint.TryConstrain(puzzle, graph)); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(0, 1), new int[] { 1, 2, 3, 4 }, graph); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(0, 2), new int[] { 1, 2, 3, 4 }, graph); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(1, 0), new int[] { 1, 2, 3, 4 }, graph); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(1, 3), new int[] { 1, 2, 3, 4 }, graph); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(2, 0), new int[] { 1, 2, 3, 4 }, graph); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(2, 3), new int[] { 1, 2, 3, 4 }, graph); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(3, 1), new int[] { 1, 2, 3, 4 }, graph); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(3, 2), new int[] { 1, 2, 3, 4 }, graph); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(1, 1), new int[] { 2, 4 }, graph); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(3, 3), new int[] { 2, 4 }, graph); ExactCoverGraphs.AssertPossibleValuesAtSquare(new(0, 3), new int[] { 1 }, graph); ExactCoverGraphs.AssertNoPossibleValuesAtSquare(new(0, 0), graph); ExactCoverGraphs.AssertNoPossibleValuesAtSquare(new(2, 2), graph); ExactCoverGraphs.AssertNoPossibleValuesAtSquare(new(1, 2), graph); ExactCoverGraphs.AssertNoPossibleValuesAtSquare(new(2, 1), graph); ExactCoverGraphs.AssertNoPossibleValuesAtSquare(new(3, 0), graph); }
public void CascadingDropUpDownUpWithDropAtMidpoint() { var puzzle = new Puzzle(4); var matrix = ExactCoverGraph.Create(puzzle); var possibilities = Possibilities.CreatePossibilities(new Coordinate(), 3); var required = Objective.CreateFullyConnected(matrix, new ReadOnlySpan <Possibility>(possibilities), 1); var fakePossibility = new FakePossibility(); var optional = OptionalObjective.CreateWithPossibilities( possibilities[1..3], 2);
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); }
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)); }
public void GetPossibilitiesOnRow_ReturnsExpectedSquares() { var puzzle = new Puzzle(4); var graph = ExactCoverGraph.Create(puzzle); int rowIndex = 1; var row = graph.GetPossibilitiesOnRow(rowIndex); Assert.Equal(4, row.Length); Assert.Equal(new Coordinate(rowIndex, 0), row[0] ![0] !.Coordinate);
public void TryConstrain_IncludingDiagonals_ConstrainsCorrectly() { var puzzle = new Puzzle(new int?[][] { new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, 6, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, 3, 8, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, new int?[] { null, null, null, null, null, null, null, null, null }, }); var boxesToConstrain = new Box[] { new Box(new Coordinate(3, 3), 3), }; var constraint = new MagicSquaresConstraint( _CreateStandardPossibleValues(9), boxesToConstrain, includeDiagonals: true); var graph = ExactCoverGraph.Create(puzzle); Assert.True(constraint.TryConstrain(puzzle, graph)); // rows: 18, 27, 45 // rows: all // rows: 4 // cols: all // cols: 84 75 // cols: 1 // 124578 | 4578 | x // 123456789 | 4578 | 1 // 4 | x | x // diagonals \: 16 25 34 -> Has to be 25 when combining top left and middle possibles. // diagonals /: 18 27 45 -> Has to be 45 when combining bottom left and middle possibles. // 25 | 4578 | x // 123456789 | 5 | 1 // 4 | x | x // Note this doesn't filter further because failed optional objectives can't drop // possiblities. _AssertPossibleValuesAtSquare(new(3, 3), new int[] { 2, 5 }, graph); _AssertPossibleValuesAtSquare(new(3, 4), new int[] { 4, 5, 7, 8 }, graph); _AssertPossibleValuesAtSquare(new(4, 3), new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, graph); _AssertPossibleValuesAtSquare(new(4, 4), new int[] { 5 }, graph); _AssertPossibleValuesAtSquare(new(4, 5), new int[] { 1 }, graph); _AssertPossibleValuesAtSquare(new(5, 3), new int[] { 4 }, graph); _AssertPossibleValuesAtSquare(new(6, 3), new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, graph); }
public void TryConstrain_WithNonUniqueValuesInDiagonals_Fails() { var puzzle = new Puzzle(new int?[][] { new int?[] { 1, null, null, null }, new int?[] { null, null, null, null }, new int?[] { null, 3, 1, null }, new int?[] { null, null, null, null }, }); var graph = ExactCoverGraph.Create(puzzle); var constraint = new DiagonalUniquenessConstraint(); Assert.False(constraint.TryConstrain(puzzle, graph)); }
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 void Constrain_ReturnsExpectedConstraints() { int size = 4; int[] possibleValues = new int[] { 1, 3, 5, 7 }; var puzzle = new Puzzle(size); var matrix = ExactCoverGraph.Create(puzzle); var squareObjectives = new HashSet <Objective>(matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities()); Assert.True(new ColumnUniquenessConstraint().TryConstrain(puzzle, matrix)); Assert.Equal( size * possibleValues.Length + squareObjectives.Count, matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities().Count()); Dictionary <int, HashSet <int> > columnsToValues = new(); for (int i = 0; i < size; ++i) { columnsToValues[i] = new HashSet <int>(); } var expectedRows = new int[] { 0, 1, 2, 3 }; Assert.All(matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities(), concreteObjective => { if (squareObjectives.Contains(concreteObjective)) { return; } IObjective objective = concreteObjective; var possibilities = objective.GetUnknownDirectPossibilities().Cast <Possibility>().ToArray(); int column = possibilities[0].Coordinate.Column; int value = possibilities[0].Index; Assert.DoesNotContain(value, columnsToValues[column]); columnsToValues[column].Add(value); var expectedCoordinates = expectedRows.Select(row => new Coordinate(row, column)).ToArray(); Assert.Equal(expectedCoordinates.Length, possibilities.Length); Assert.All(possibilities, p => { Assert.Contains(p.Coordinate, expectedCoordinates); Assert.Equal(value, p.Index); }); Assert.All(expectedCoordinates, c => Assert.NotNull(possibilities.SingleOrDefault(p => p.Coordinate == c))); }); Assert.All( columnsToValues.Values, values => Assert.Equal(new HashSet <int> { 0, 1, 2, 3 }, values)); }
public void Constrain_GroupsConstraintsAsExpected() { int size = 4; int boxSize = 2; int[] possibleValues = new int[] { 1, 3, 5, 7 }; var puzzle = new Puzzle(size); var matrix = ExactCoverGraph.Create(puzzle); var squareObjectives = new HashSet <Objective>(matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities()); Assert.True(new BoxUniquenessConstraint().TryConstrain(puzzle, matrix)); Assert.Equal( size * possibleValues.Length + squareObjectives.Count, matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities().Count()); Dictionary <int, HashSet <int> > boxIndicesToValues = new(); for (int i = 0; i < size; ++i) { boxIndicesToValues[i] = new HashSet <int>(); } Assert.All(matrix.GetUnsatisfiedRequiredObjectivesWithConcretePossibilities(), concreteObjective => { if (squareObjectives.Contains(concreteObjective)) { return; } IObjective objective = concreteObjective; var possibilities = objective.GetUnknownDirectPossibilities().Cast <Possibility>().ToArray(); int boxIndex = Boxes.CalculateBoxIndex(possibilities[0].Coordinate, boxSize); int value = possibilities[0].Index; Assert.DoesNotContain(value, boxIndicesToValues[boxIndex]); boxIndicesToValues[boxIndex].Add(value); var boxCoordinates = Boxes.YieldUnsetCoordsForBox(boxIndex, boxSize, puzzle).ToArray(); Assert.Equal(boxCoordinates.Length, possibilities.Length); Assert.All(possibilities, p => { Assert.Contains(p.Coordinate, boxCoordinates); Assert.Equal(value, p.Index); }); Assert.All(boxCoordinates, c => Assert.NotNull(possibilities.SingleOrDefault(p => p.Coordinate == c))); }); Assert.All( boxIndicesToValues.Values, values => Assert.Equal(new HashSet <int> { 0, 1, 2, 3 }, values)); }
/// <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); }
/// <inheritdoc/> public bool TryConstrain(IReadOnlyPuzzle puzzle, ExactCoverGraph graph) { if (!Boxes.TryIntSquareRoot(puzzle.Size, out int boxSize)) { return(false); } for (int box = 0; box < puzzle.Size; box++) { if (!_TryAppendRequirementsInBox(box, boxSize, puzzle, graph)) { return(false); } } return(true); }
public void GetAllPossibilitiesAt_ForUnsetCoordinate_ReturnsExpectedPossibilities() { var puzzle = new Puzzle(4); ExactCoverGraph graph = ExactCoverGraph.Create(puzzle); var possibilities = graph.GetAllPossibilitiesAt(new Coordinate()); Assert.NotNull(possibilities); Assert.Equal(puzzle.Size, possibilities !.Length); for (int i = 0; i < puzzle.Size; ++i) { Assert.NotNull(possibilities[i]); Assert.Equal(i, possibilities[i] !.Index); } }
public void CreateFullyConnected_WithOptionalObjectives_ConnectsCorrectly() { var puzzle = new Puzzle(4); var matrix = ExactCoverGraph.Create(puzzle); var optionalObjectives = new OptionalObjective[] { OptionalObjective.CreateWithPossibilities(Possibilities.CreatePossibilities(new Coordinate(), 2), 1), OptionalObjective.CreateWithPossibilities(Possibilities.CreatePossibilities(new Coordinate(), 2), 1), }; var concreteObjective = Objective.CreateFullyConnected(matrix, optionalObjectives, 1); IObjective objective = concreteObjective; Assert.Equal( new HashSet <IPossibility>(optionalObjectives), new HashSet <IPossibility>(objective.GetUnknownDirectPossibilities())); }
internal static void AssertNoPossibleValuesAtSquare(Coordinate coord, ExactCoverGraph graph) { var square = graph.GetAllPossibilitiesAt(coord); if (square is not null) { for (int valueIndex = 0; valueIndex < square.Length; ++valueIndex) { var possibleValue = square[valueIndex]; if (possibleValue is not null) { Assert.Equal(NodeState.DROPPED, possibleValue.State); } } } }
/// <inheritdoc/> public bool TryConstrain(IReadOnlyPuzzle puzzle, ExactCoverGraph graph) { 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); } if (!ConstraintUtil.TryImplementUniquenessConstraintForSquares(puzzle, columnCoordinates, graph)) { return(false); } } return(true); }
public void AllUnknownPossibilitiesAreConcrete_UpdatesWhenPossibilitiesChange() { var puzzle = new Puzzle(4); var matrix = ExactCoverGraph.Create(puzzle); var fakePossibility = new FakePossibility(isConcrete: false); var possibilities = new IPossibility[] { new Possibility(new Coordinate(), 0), fakePossibility, }; var objective = Objective.CreateFullyConnected(matrix, possibilities, 1); Assert.Single(fakePossibility.AttachedObjectives); Assert.False(objective.AllUnknownPossibilitiesAreConcrete); Assert.True(((IObjective)objective).TryDropPossibility(fakePossibility.AttachedObjectives.First())); Assert.True(objective.AllUnknownPossibilitiesAreConcrete); }
public void TrySelectAndDeselect_WhenSharedByOpposingObjectives_Fails() { var puzzle = new Puzzle(4); var matrix = ExactCoverGraph.Create(puzzle); var concretePossibility = new Possibility(new(), 1); var possibilities = new IPossibility[] { concretePossibility }; var parentA = OptionalObjective.CreateWithPossibilities(possibilities, 1); var parentB = OptionalObjective.CreateWithPossibilities(possibilities.Append(new FakePossibility()).ToArray(), 1); var required = Objective.CreateFullyConnected( matrix, new IPossibility[] { parentA, parentB }, 1); Assert.False(concretePossibility.TrySelect()); Assert.Equal(NodeState.UNKNOWN, parentA.State); Assert.Equal(NodeState.UNKNOWN, parentB.State); Assert.NotEqual(NodeState.SELECTED, required.State); }
private static bool _TryAppendRequirementsInBox( int box, int boxSize, IReadOnlyPuzzle puzzle, ExactCoverGraph graph) { Coordinate startCoord = Boxes.GetStartingBoxCoordinate(box, boxSize); var endCoord = new Coordinate( startCoord.Row + boxSize, startCoord.Column + 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); } } return(ConstraintUtil.TryImplementUniquenessConstraintForSquares(puzzle, boxCoordinates, graph)); }
public void TryDropPossibility_WhenRequired_Fails() { var puzzle = new Puzzle(4); var matrix = ExactCoverGraph.Create(puzzle); var possibilities = new FakePossibility[] { new FakePossibility(), new FakePossibility(), new FakePossibility(), }; var toDrop = possibilities[0]; var concreteObjective = Objective.CreateFullyConnected(matrix, possibilities, 3); IObjective objective = concreteObjective; Assert.False(objective.TryDropPossibility(toDrop.AttachedObjectives.First())); Assert.Equal(3, concreteObjective.CountUnknown); Assert.Equal(3, objective.GetUnknownDirectPossibilities().Count()); Assert.Contains(toDrop, objective.GetUnknownDirectPossibilities()); Assert.Contains(possibilities[1], objective.GetUnknownDirectPossibilities()); Assert.Contains(possibilities[2], objective.GetUnknownDirectPossibilities()); }
/// <inheritdoc/> public bool TryConstrain(IReadOnlyPuzzle puzzle, ExactCoverGraph graph) { Span <bool> isConstraintSatisfiedAtIndex = stackalloc bool[graph.AllPossibleValues.Length]; for (int row = 0; row < puzzle.Size; row++) { ReadOnlySpan <Possibility?[]?> rowSquares = graph.GetPossibilitiesOnRow(row); isConstraintSatisfiedAtIndex.Clear(); for (int col = 0; col < puzzle.Size; col++) { int?puzzleValue = puzzle[row, col]; if (puzzleValue.HasValue) { int valueIndex = graph.ValuesToIndices[puzzleValue.Value]; if (isConstraintSatisfiedAtIndex[valueIndex]) { return(false); } isConstraintSatisfiedAtIndex[valueIndex] = true; } } for (int possibilityIndex = 0; possibilityIndex < isConstraintSatisfiedAtIndex.Length; possibilityIndex++) { if (isConstraintSatisfiedAtIndex[possibilityIndex]) { if (!ConstraintUtil.TryDropPossibilitiesAtIndex(rowSquares, possibilityIndex)) { return(false); } continue; } if (!ConstraintUtil.TryAddObjectiveForPossibilityIndex( rowSquares, possibilityIndex, graph, requiredCount: 1, objective: out _)) { return(false); } } } return(true); }
public void CascadingDropUpDownUp() { var puzzle = new Puzzle(4); var matrix = ExactCoverGraph.Create(puzzle); var possibilities = Possibilities.CreatePossibilities(new Coordinate(), 2); var required = Objective.CreateFullyConnected(matrix, new ReadOnlySpan <Possibility>(possibilities), 1); var fakePossibility = new FakePossibility(); var optional = OptionalObjective.CreateWithPossibilities( possibilities.Cast <IPossibility>().ToArray(), 2); var fakeLinkToOptional = Link.CreateConnectedLink(fakePossibility, optional); var separateRequired = Objective.CreateFullyConnected(matrix, new IPossibility[] { optional }, 1); // Additionally try one where optional ends up dropped before the cascade somehow... maybe add one to possibilities and to optional's countToSatisfy. Assert.True(possibilities[0].TrySelect()); Assert.Equal(NodeState.SELECTED, possibilities[0].State); Assert.Equal(NodeState.DROPPED, possibilities[1].State); Assert.Equal(NodeState.UNKNOWN, optional.State); Assert.Empty(fakePossibility.DroppedFromObjectives); Assert.Equal(NodeState.SELECTED, required.State); Assert.NotEqual(NodeState.SELECTED, separateRequired.State); Assert.True(((IObjective)optional).TrySelectPossibility(fakeLinkToOptional)); Assert.Equal(NodeState.SELECTED, possibilities[0].State); Assert.Equal(NodeState.DROPPED, possibilities[1].State); Assert.Equal(NodeState.SELECTED, optional.State); Assert.Equal(NodeState.SELECTED, required.State); Assert.Equal(NodeState.SELECTED, separateRequired.State); ((IObjective)optional).DeselectPossibility(fakeLinkToOptional); Assert.Equal(NodeState.SELECTED, possibilities[0].State); Assert.Equal(NodeState.DROPPED, possibilities[1].State); Assert.Equal(NodeState.UNKNOWN, optional.State); Assert.Empty(fakePossibility.DroppedFromObjectives); Assert.Equal(NodeState.SELECTED, required.State); Assert.NotEqual(NodeState.SELECTED, separateRequired.State); Assert.False(((IObjective)optional).TryDropPossibility(fakeLinkToOptional)); }