Beispiel #1
0
 /// <summary>
 /// Constructs a constraint that will enforce that the given <paramref name="squares"/> are
 /// magic squares based on the rows, columns, and, optionally, the diagonals.
 /// </summary>
 /// <param name="possibleValues">
 /// The possible values that can be in the magic squares.
 /// </param>
 /// <param name="squares">
 /// The locations of the magic squares.
 /// </param>
 /// <param name="includeDiagonals">
 /// If true, values along the diagonals of the square must also sum to the magic number.
 /// </param>
 /// <exception cref="ArgumentException">
 /// If the any of the given <paramref name="squares"/>' sizes are not compatible with the
 /// length of <paramref name="possibleValues"/>.
 /// </exception>
 public MagicSquaresConstraint(ReadOnlySpan <int> possibleValues, IEnumerable <Box> squares, bool includeDiagonals = true)
 {
     _size             = possibleValues.Length;
     _magicSquares     = squares.ToArray();
     _squareSize       = Boxes.IntSquareRoot(_size);
     _includeDiagonals = includeDiagonals;
     if (_magicSquares.Any(
             b =>
             b.TopLeft.Row < 0 || b.TopLeft.Column < 0 ||
             b.TopLeft.Row + b.Size > _size || b.TopLeft.Column + b.Size > _size ||
             b.Size != _squareSize))
     {
         throw new ArgumentException(
                   $"Based on the {nameof(possibleValues)}, {nameof(squares)} must fit in a puzzle of size {_size} and have size {_squareSize}.");
     }
     _allPossibleValues = new BitVector();
     for (int i = 0; i < possibleValues.Length; ++i)
     {
         if (_allPossibleValues.IsBitSet(possibleValues[i]))
         {
             throw new ArgumentException("Values must be unique.");
         }
         _allPossibleValues.SetBit(possibleValues[i]);
     }
     _possibleSets = MagicSquares.ComputeSets(possibleValues, _squareSize, _allPossibleValues);
 }
 /// <inheritdoc/>
 public bool TryInitFor(IReadOnlyPuzzleWithMutablePossibleValues puzzle)
 {
     _boxSize = Boxes.IntSquareRoot(puzzle.Size);
     _puzzle  = puzzle;
     _helper  = new UniqueInXHelper(puzzle);
     return(true);
 }
 /// <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);
 }
        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 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);
        }
Beispiel #6
0
        /// <inheritdoc/>
        public bool TryInit(IReadOnlyPuzzle puzzle, BitVector uniquePossibleValues)
        {
            int size = puzzle.Size;

            _boxSize = Boxes.IntSquareRoot(size);
            if (size != _unsetRowValues?.Length)
            {
                _unsetRowValues = new BitVector[size];
                _unsetRowValues.AsSpan().Fill(uniquePossibleValues);
                _unsetColValues = _unsetRowValues.AsSpan().ToArray();
                _unsetBoxValues = _unsetRowValues.AsSpan().ToArray();
            }
            else
            {
                _unsetRowValues.AsSpan().Fill(uniquePossibleValues);
                _unsetColValues.AsSpan().Fill(uniquePossibleValues);
                _unsetBoxValues.AsSpan().Fill(uniquePossibleValues);
            }
            int boxIdx = 0;

            for (int row = 0; row < size; row++)
            {
                for (int col = 0; col < size; col++)
                {
                    if (col == 0)
                    {
                        boxIdx = (row / _boxSize) * _boxSize;
                    }
                    else if (col % _boxSize == 0)
                    {
                        boxIdx++;
                    }
                    int?val = puzzle[row, col];
                    if (!val.HasValue)
                    {
                        continue;
                    }
                    if (!_unsetRowValues[row].IsBitSet(val.Value) ||
                        !_unsetColValues ![col].IsBitSet(val.Value) ||
Beispiel #7
0
        public void GetPossibleValues_MatchesGetPossibleBoxValues()
        {
            var puzzle = new PuzzleWithPossibleValues(new int?[][] {
                new int?[] { 1, null /* 4 */, null /* 3 */, 2 },
                new int?[] { null /* 2 */, null /* 3 */, null /* 1 */, 4 },
                new int?[] { null /* 4 */, 1, null /* 2 */, 3 },
                new int?[] { 3, null /* 2 */, null /* 4 */, 1 }
            });
            var rule = new MaxCountPerBoxRule();

            Assert.True(rule.TryInit(puzzle, puzzle.UniquePossibleValues));

            for (int row = 0; row < puzzle.Size; row++)
            {
                for (int column = 0; column < puzzle.Size; column++)
                {
                    int box = Boxes.CalculateBoxIndex(new(row, column), Boxes.IntSquareRoot(puzzle.Size));
                    Assert.Equal(
                        rule.GetMissingValuesForBox(box),
                        rule.GetPossibleValues(new Coordinate(row, column)));
                }
            }
        }
        public void GetPossibleValues_MatchesGetPossibleBoxValues()
        {
            var puzzle = new PuzzleWithPossibleValues(new int?[][] {
                new int?[] { 1, null /* 4 */, null /* 3 */, 2 },
                new int?[] { null /* 2 */, null /* 3 */, null /* 1 */, 4 },
                new int?[] { null /* 4 */, 1, null /* 2 */, 3 },
                new int?[] { 3, null /* 2 */, null /* 4 */, 1 }
            });
            var rule = new BoxUniquenessRule();

            Assert.True(rule.TryInit(puzzle, puzzle.UniquePossibleValues));
            IList <BitVector> possibleValuesByBox = _GetPossibleValuesByBox(puzzle.Size, rule);

            for (int row = 0; row < puzzle.Size; row++)
            {
                for (int column = 0; column < puzzle.Size; column++)
                {
                    int box = Boxes.CalculateBoxIndex(new(row, column), Boxes.IntSquareRoot(puzzle.Size));
                    Assert.Equal(
                        possibleValuesByBox[box],
                        rule.GetPossibleValues(new Coordinate(row, column)));
                }
            }
        }
Beispiel #9
0
        internal static void AssertStandardPuzzleSolved(IReadOnlyPuzzle puzzle)
        {
            Assert.Equal(0, puzzle.NumEmptySquares);
            var alreadyFound = new HashSet <int>(puzzle.Size);

            for (int row = 0; row < puzzle.Size; row++)
            {
                alreadyFound.Clear();
                for (int col = 0; col < puzzle.Size; col++)
                {
                    Assert.True(alreadyFound.Add(puzzle[row, col].Value), $"Value at ({row}, {col}) clashed with another value in that row!");
                }
            }
            for (int col = 0; col < puzzle.Size; col++)
            {
                alreadyFound.Clear();
                for (int row = 0; row < puzzle.Size; row++)
                {
                    Assert.True(alreadyFound.Add(puzzle[row, col].Value), $"Value at ({row}, {col}) clashed with another value in that col!");
                }
            }
            int boxSize = Boxes.IntSquareRoot(puzzle.Size);

            for (int box = 0; box < puzzle.Size; box++)
            {
                alreadyFound.Clear();
                (int startRow, int startCol) = Boxes.GetStartingBoxCoordinate(box, boxSize);
                for (int row = startRow; row < startRow + boxSize; row++)
                {
                    for (int col = startCol; col < startCol + boxSize; col++)
                    {
                        Assert.True(alreadyFound.Add(puzzle[row, col].Value), $"Value at ({row}, {col}) clashed with another value in that box!");
                    }
                }
            }
        }
Beispiel #10
0
 public void IntSquareRoot_WithInvalidSize_Throws(int puzzleSize)
 {
     Assert.Throws <ArgumentException>(() => Boxes.IntSquareRoot(puzzleSize));
 }
Beispiel #11
0
 public void IntSquareRoot_WithValidSizes_IsCorrect(int puzzleSize, int expectedBoxSize)
 {
     Assert.Equal(expectedBoxSize, Boxes.IntSquareRoot(puzzleSize));
 }