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 TryInit(IReadOnlyPuzzleWithMutablePossibleValues puzzle) { int size = puzzle.Size; _puzzle = puzzle; _boxSize = Boxes.IntSquareRoot(puzzle.Size); _unsetRowValues = new BitVector[size]; _unsetRowValues.AsSpan().Fill(_puzzle.UniquePossibleValues); Span <BitVector> possibleValues = _unsetRowValues.AsSpan(); _unsetColumnValues = possibleValues.ToArray(); _unsetBoxValues = possibleValues.ToArray(); int boxIdx; for (int row = 0; row < size; row++) { for (int col = 0; col < size; col++) { boxIdx = Boxes.CalculateBoxIndex(new Coordinate(row, col), _boxSize); int?val = puzzle[row, col]; if (!val.HasValue) { continue; } if (!_unsetRowValues[row].IsBitSet(val.Value) || !_unsetColumnValues[col].IsBitSet(val.Value) || !_unsetBoxValues[boxIdx].IsBitSet(val.Value)) { return(false); } _unsetRowValues[row].UnsetBit(val.Value); _unsetColumnValues[col].UnsetBit(val.Value); _unsetBoxValues[boxIdx].UnsetBit(val.Value); } } foreach (Coordinate c in puzzle.GetUnsetCoords()) { _puzzle.IntersectPossibleValues(in c, _GetPossibleValues(in c)); if (_puzzle.GetPossibleValues(in c).IsEmpty) { return(false); } } return(true); }