Пример #1
0
 /// <summary>Override; see base.</summary>
 public override ConstraintResult Process(SolverState state)
 {
     for (var ix = 0; ix < Area1.Length; ix++)
     {
         state.MarkImpossible(Area1[ix], value => state.IsImpossible(Area2[ix], value));
         state.MarkImpossible(Area2[ix], value => state.IsImpossible(Area1[ix], value));
     }
     return(null);
 }
Пример #2
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            // This can only happen if the user has specified a single region at the start.
            if (Regions.Length == 1)
            {
                return new[] { new SumConstraint(Sum, Regions[0]) }
            }
            ;

            // Find out which regions can now be ruled out
            List <int[]> newRegions = null;

            for (var i = 0; i < Regions.Length; i++)
            {
                var region = Regions[i];
                int min, max;
                if ((min = region.Sum(state.MinPossible)) > Sum || (max = region.Sum(state.MaxPossible)) < Sum)
                {
                    if (newRegions == null)
                    {
                        newRegions = new List <int[]>(Regions.Take(i));
                    }
                }
                else
                {
                    if (min == Sum && max == Sum)   // The constraint is already satisfied
                    {
                        return(ConstraintResult.Remove);
                    }
                    if (newRegions != null)
                    {
                        newRegions.Add(region);
                    }
                }
            }
            if (newRegions != null)
            {
                if (newRegions.Count == 0)
                {
                    // This can only happen if some regions overlap and the algorithm filled in one of the shared cells.
                    return(ConstraintResult.Violation);
                }
                else if (newRegions.Count == 1)
                {
                    return new[] { new SumConstraint(Sum, newRegions[0]) }
                }
                ;
                else
                {
                    return new[] { new SumAlternativeConstraint(Sum, newRegions) }
                };
            }
            return(null);
        }
    }
}
Пример #3
0
 /// <summary>Override; see base.</summary>
 public override ConstraintResult Process(SolverState state)
 {
     if (state.LastPlacedCell == AffectedCells[0])
     {
         state.MarkImpossible(AffectedCells[1], value => !IsValid(state.LastPlacedValue, value));
     }
     else if (state.LastPlacedCell == AffectedCells[1])
     {
         state.MarkImpossible(AffectedCells[0], value => !IsValid(value, state.LastPlacedValue));
     }
     return(null);
 }
Пример #4
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            state.MarkImpossible(AffectedCells[0], value => value < 0 || value >= AffectedCells.Length || state.IsImpossible(AffectedCells[value], Value));

            if (state.LastPlacedCell == AffectedCells[0])
            {
                // The focus cell has been set, therefore place the value in the correct position
                state.MustBe(AffectedCells[state.LastPlacedValue], Value);
            }

            return(null);
        }
Пример #5
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            if (state.LastPlacedCell != null && !AffectedCells.Contains(state.LastPlacedCell.Value))
            {
                return(null);
            }

            var innerTakens = new bool[Subconstraints.Length][][];
            List <Constraint> newSubconstraints = null;

            for (var sc = 0; sc < Subconstraints.Length; sc++)
            {
                var substate = new SolverStateImpl {
                    Parent = state, Takens = Ut.NewArray <bool>(state.GridSize, state.MaxValue - state.MinValue + 1)
                };
                var result = Subconstraints[sc].Process(substate);
                innerTakens[sc] = substate.Takens;

                if (result is ConstraintReplace)
                {
                    throw new NotImplementedException("The OrConstraint does not support subconstraints that replace themselves with new constraints.");
                }
                else if (result is ConstraintViolation)
                {
                    if (newSubconstraints == null)
                    {
                        newSubconstraints = new List <Constraint>(Subconstraints.Take(sc));
                    }
                }
                else if (newSubconstraints != null)
                {
                    newSubconstraints.Add(Subconstraints[sc]);
                }
            }

            foreach (var cell in AffectedCells)
            {
                state.MarkImpossible(cell, value => innerTakens.All(taken => taken == null || taken[cell][value - state.MinValue]));
            }

            if (newSubconstraints != null)
            {
                return new[] { new OrConstraint(newSubconstraints) }
            }
            ;
            return(null);
        }
    }
Пример #6
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            var min = state.MinPossible(AffectedCells[0]) + 1;

            for (var i = 1; i < AffectedCells.Length; i++)
            {
                state.MarkImpossible(AffectedCells[i], value => value < min);
                min = state.MinPossible(AffectedCells[i]) + 1;
            }
            var max = state.MaxPossible(AffectedCells[AffectedCells.Length - 1]) - 1;

            for (var i = AffectedCells.Length - 2; i >= 0; i--)
            {
                state.MarkImpossible(AffectedCells[i], value => value > max);
                max = state.MaxPossible(AffectedCells[i]) - 1;
            }
            return(null);
        }
Пример #7
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            if (state.LastPlacedCell == null)
            {
                return(null);
            }

            var unknowns = AffectedCells.Count(af => state[af] == null);

            if (unknowns == 1)
            {
                var unknown = AffectedCells.First(af => state[af] == null);

                state.MarkImpossible(unknown, value => !IsValid(
                                         state[AffectedCells[0]] ?? value,
                                         state[AffectedCells[1]] ?? value,
                                         state[AffectedCells[2]] ?? value,
                                         state[AffectedCells[3]] ?? value
                                         ));
            }
            else if (unknowns == 2)
            {
                var unkn1     = AffectedCells.First(af => state[af] == null);
                var unkn1Ix   = AffectedCells.IndexOf(unkn1);
                var unkn2     = AffectedCells.Last(af => state[af] == null);
                var numValues = state.MaxValue - state.MinValue + 1;
                var possibles = new bool[numValues * numValues];
                for (var val1 = state.MinValue; val1 <= state.MaxValue; val1++)
                {
                    for (var val2 = state.MinValue; val2 <= state.MaxValue; val2++)
                    {
                        possibles[(val1 - state.MinValue) + numValues * (val2 - state.MinValue)] = IsValid(
                            state[AffectedCells[0]] ?? (unkn1Ix == 0 ? val1 : val2),
                            state[AffectedCells[1]] ?? (unkn1Ix == 1 ? val1 : val2),
                            state[AffectedCells[2]] ?? (unkn1Ix == 2 ? val1 : val2),
                            state[AffectedCells[3]] ?? (unkn1Ix == 3 ? val1 : val2));
                    }
                }
                state.MarkImpossible(unkn1, value1 => Enumerable.Range(0, numValues).All(value2 => !possibles[(value1 - state.MinValue) + numValues * value2]));
                state.MarkImpossible(unkn2, value2 => Enumerable.Range(0, numValues).All(value1 => !possibles[value1 + numValues * (value2 - state.MinValue)]));
            }

            return(null);
        }
Пример #8
0
 /// <summary>Override; see base.</summary>
 public override sealed ConstraintResult Process(SolverState state)
 {
     for (var cellIx = 0; cellIx < (state.LastPlacedCell != null ? 1 : AffectedCells != null ? AffectedCells.Length : state.GridSize); cellIx++)
     {
         var cell = state.LastPlacedCell ?? (AffectedCells != null ? AffectedCells[cellIx] : cellIx);
         if (state[cell] == null || (AffectedValues != null && !AffectedValues.Contains(state[cell].Value)))
         {
             continue;
         }
         foreach (var relatedCell in getRelatedCells(cell))
         {
             if (EnforcedCells == null || EnforcedCells.Contains(relatedCell) || EnforcedCells.Contains(cell))
             {
                 state.MarkImpossible(relatedCell, state[cell].Value);
             }
         }
     }
     return(null);
 }
Пример #9
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            var minPossibleSum = AffectedCells.Sum(state.MinPossible);
            var maxPossibleSum = AffectedCells.Sum(state.MaxPossible);

            for (var ix = 0; ix < AffectedCells.Length; ix++)
            {
                var cell = AffectedCells[ix];
                if (state[cell] != null)
                {
                    continue;
                }
                var minOther = minPossibleSum - state.MinPossible(cell);
                var maxOther = maxPossibleSum - state.MaxPossible(cell);
                state.MarkImpossible(cell, value => minOther + value > Sum || maxOther + value < Sum);
            }

            return(null);
        }
Пример #10
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            // Determine if we know the offset
            for (var i = 0; i < Area1.Length; i++)
            {
                if (state[Area1[i]] != null && state[Area2[i]] != null)
                {
                    // We found the offset. Process the entire region and stop here.
                    var offset = state[Area2[i]].Value - state[Area1[i]].Value;
                    for (var ix = 0; ix < Area1.Length; ix++)
                    {
                        state.MarkImpossible(Area1[ix], value => state.IsImpossible(Area2[ix], value + offset));
                        state.MarkImpossible(Area2[ix], value => state.IsImpossible(Area1[ix], value - offset));
                    }
                    return(null);
                }
            }

            // We do not know the offset.
            return(null);
        }
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            if (state.LastPlacedCell == null)
            {
                return(null);
            }

            var unknowns = AffectedCells.Count(af => state[af] == null);

            if (unknowns != 1)
            {
                return(null);
            }
            var unknown = AffectedCells.First(af => state[af] == null);

            state.MarkImpossible(unknown, value => !IsValid(
                                     state[AffectedCells[0]] ?? value,
                                     state[AffectedCells[1]] ?? value,
                                     state[AffectedCells[2]] ?? value
                                     ));
            return(null);
        }
Пример #12
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            if (state.LastPlacedCell != null && !AffectedCells.Contains(state.LastPlacedCell.Value))
            {
                return(null);
            }

            var productAlready  = 1;
            var cellsLeftToFill = 0;

            foreach (var cell in AffectedCells)
            {
                if (state[cell] is int value)
                {
                    productAlready *= value;
                }
                else
                {
                    cellsLeftToFill++;
                }
            }
            if (cellsLeftToFill == 0 || (productAlready == 0 && Product == 0))
            {
                return(null);
            }

            var alreadyBroken = productAlready == 0 || (Product % productAlready != 0);

            foreach (var cell in AffectedCells)
            {
                state.MarkImpossible(cell, value =>
                                     alreadyBroken ||
                                     // The last remaining cell must have the exact required value
                                     (cellsLeftToFill == 1 && productAlready * value != Product) ||
                                     // The remaining cells must be factors of whatever is left to multiply
                                     (cellsLeftToFill > 1 && value == 0 ? (Product != 0) : ((Product / productAlready) % value != 0)));
            }
            return(null);
        }
Пример #13
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            var minPossibleTarget = state.MinPossible(SumCell);
            var maxPossibleTarget = state.MaxPossible(SumCell);
            var minPossibleSum    = Region.Sum(state.MinPossible);
            var maxPossibleSum    = Region.Sum(state.MaxPossible);

            // Constrain the sum cell
            if (state[SumCell] == null)
            {
                state.MarkImpossible(SumCell, value => value <minPossibleSum || value> maxPossibleSum);
            }

            // Constrain the operand cells
            for (var ix = 0; ix < Region.Length; ix++)
            {
                var cell     = Region[ix];
                var minOther = minPossibleSum - state.MinPossible(cell);
                var maxOther = maxPossibleSum - state.MaxPossible(cell);
                state.MarkImpossible(cell, value => minOther + value > maxPossibleTarget || maxOther + value < minPossibleTarget);
            }

            return(null);
        }
Пример #14
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            if (state.LastPlacedCell is int cell)
            {
                var x      = cell % GridWidth;
                var y      = cell / GridWidth;
                var refMod = (state[cell].Value % Modulo + Modulo) % Modulo;

                for (var dx = -1; dx <= 1; dx++)
                {
                    if (x + dx >= 0 && x + dx < GridWidth)
                    {
                        for (var dy = (dx == 0 ? -1 : 0); dy <= (dx == 0 ? 1 : 0); dy++)
                        {
                            if (y + dy >= 0 && y + dy < GridHeight)
                            {
                                state.MarkImpossible((x + dx) + GridWidth * (y + dy), v => (v % Modulo + Modulo) % Modulo == refMod);
                            }
                        }
                    }
                }
            }
            return(null);
        }
Пример #15
0
        public override ConstraintResult Process(SolverState state)
        {
            var ix = state.LastPlacedCell;

            if (state.LastPlacedCell == null)
            {
                // The inside of the line cannot contain a 1 or a 9
                for (var icIx = 0; icIx < InnerCells.Length; icIx++)
                {
                    state.MarkImpossible(InnerCells[icIx], 1);
                    state.MarkImpossible(InnerCells[icIx], 9);
                }
                return(null);
            }

            // If both caps are filled in, all the inner cells must simply be between them.
            if (state[Cap1] != null && state[Cap2] != null)
            {
                // We don’t need to recompute this multiple times.
                if (!(ix == Cap1 || ix == Cap2))
                {
                    return(null);
                }
                var min = Math.Min(state[Cap1].Value, state[Cap2].Value);
                var max = Math.Max(state[Cap1].Value, state[Cap2].Value);
                for (var icIx = 0; icIx < InnerCells.Length; icIx++)
                {
                    state.MarkImpossible(InnerCells[icIx], v => v <= min || v >= max);
                }
            }
            // If one cap is filled in, all the inner cells must simply be different from it.
            else if (state[Cap1] != null || state[Cap2] != null)
            {
                // We don’t need to recompute this multiple times.
                if (!(ix == Cap1 || ix == Cap2))
                {
                    return(null);
                }
                var v = state[Cap1] ?? state[Cap2].Value;
                for (var icIx = 0; icIx < InnerCells.Length; icIx++)
                {
                    state.MarkImpossible(InnerCells[icIx], v);
                }
            }

            var curMin = InnerCells.Where(c => state[c] != null).MinOrNull(c => state[c].Value);

            if (curMin == null)
            {
                return(null);
            }
            var curMax = InnerCells.Where(c => state[c] != null).Max(c => state[c].Value);

            // If neither cap is filled in, both must be outside the range but we don’t yet know which one is the min and which one is the max
            if (state[Cap1] == null && state[Cap2] == null)
            {
                state.MarkImpossible(Cap1, v => v >= curMin.Value && v <= curMax);
                state.MarkImpossible(Cap2, v => v >= curMin.Value && v <= curMax);
            }
            else
            {
                // Check if Cap1 is filled in and Cap2 not, and then also check the reverse
                var cap1 = Cap1;
                var cap2 = Cap2;
iter:
                if (state[cap1] == null && state[cap2] != null)
                {
                    if (state[cap2].Value > curMax)
                    {
                        // grid[cap1] must be < curMin
                        state.MarkImpossible(cap1, v => v >= curMin.Value);
                    }
                    else if (state[cap2].Value < curMin.Value)
                    {
                        // grid[cap1] must be > curMax
                        state.MarkImpossible(cap1, v => v <= curMax);
                    }
                    else
                    {
                        throw new InvalidOperationException("BetweenLineConstraint encountered an internal bug.");
                    }
                }
                if (cap1 == Cap1)
                {
                    cap1 = Cap2;
                    cap2 = Cap1;
                    goto iter;
                }
            }
            return(null);
        }
Пример #16
0
 /// <summary>
 ///     Constraint implementations must use <see cref="SolverState.MarkImpossible(int, int)"/> to mark values that are
 ///     known to be impossible given the incomplete grid exposed by <see cref="SolverState.this[int]"/>.</summary>
 /// <returns>
 ///     <para>
 ///         Implementations must return <c>null</c> if the constraint remains valid for the remainder of filling this
 ///         grid, or a collection of constraints that this constraint shall be replaced with (can be empty).</para>
 ///     <para>
 ///         For example, in <see cref="EqualSumsConstraint"/>, since the sum is not initially known, the constraint
 ///         waits until one of its regions is filled and then uses this return value to replace itself with several
 ///         <see cref="SumConstraint"/>s to ensure the other regions have the same sum.</para>
 ///     <para>
 ///         The algorithm will automatically call this method again on all the new constraints for all cells already
 ///         placed in the grid. The constraints returned MUST NOT themselves return yet more constraints at that
 ///         point.</para></returns>
 public abstract ConstraintResult Process(SolverState state);
Пример #17
0
 /// <summary>Override; see base.</summary>
 public override ConstraintResult Process(SolverState state) => Lambda(state);
Пример #18
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            // This will determine which values are still possible in which of the affected cells.
            var poss = Ut.NewArray(AffectedCells.Length, i => new bool[state.MaxValue - state.MinValue + 1]);

            // If any combination can be ruled out, this will contain the remaining combinations still available.
            List <int?[]> newComb = null;

            for (var i = 0; i < Combinations.Length; i++)
            {
                // Can this combination be ruled out?
                if (AffectedCells.Any((cellIx, lstIx) => Combinations[i][lstIx] != null && (state[cellIx] == null ? state.IsImpossible(cellIx, Combinations[i][lstIx].Value) : (state[cellIx].Value != Combinations[i][lstIx].Value))))
                {
                    if (newComb == null)
                    {
                        newComb = new List <int?[]>(Combinations.Take(i));
                    }
                }
                else
                {
                    if (newComb != null)
                    {
                        newComb.Add(Combinations[i]);
                    }

                    // Remember the possibilities for each cell
                    for (var lstIx = 0; lstIx < Combinations[i].Length; lstIx++)
                    {
                        if (Combinations[i][lstIx] == null)
                        {
                            poss[lstIx] = null;
                        }
                        else if (poss[lstIx] != null)
                        {
                            poss[lstIx][Combinations[i][lstIx].Value - state.MinValue] = true;
                        }
                    }
                }
            }

            // Mark any cell values that are no longer possible as taken
            for (var lstIx = 0; lstIx < poss.Length; lstIx++)
            {
                if (state[AffectedCells[lstIx]] == null && poss[lstIx] != null)
                {
                    for (var v = 0; v < poss[lstIx].Length; v++)
                    {
                        if (!poss[lstIx][v] && !state.IsImpossible(AffectedCells[lstIx], v + state.MinValue))
                        {
                            state.MarkImpossible(AffectedCells[lstIx], v + state.MinValue);
                        }
                    }
                }
            }

            if (newComb != null)
            {
                return new[] { new CombinationsConstraint(AffectedCells, newComb.ToArray()) }
            }
            ;
            return(null);
        }
Пример #19
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            if (state.LastPlacedCell is int cell)
            {
                var innerCell           = AffectedCells == null ? cell : AffectedCells.IndexOf(cell);
                var x                   = innerCell % SideLength;
                var y                   = innerCell / SideLength;
                var numUnknownsInColumn = Enumerable.Range(0, SideLength).Count(row => state[coord(x, row)] == null);
                if (numUnknownsInColumn < 2)
                {
                    // We just placed the last or second-last value in this column.
                    // If it’s the last, we need to make sure that no almost-complete column is about to be filled with the same parities.
                    // If it’s the second-last, we need to make sure that THIS column isn’t going to be filled with the same parities as another equal column.
                    for (var col = 0; col < SideLength; col++)
                    {
                        if (col == x)
                        {
                            continue;
                        }

                        var discrepantRow = -1;
                        for (var row = 0; row < SideLength; row++)
                        {
                            if (state[coord(numUnknownsInColumn == 0 ? col : x, row)] == null && state[coord(numUnknownsInColumn == 0 ? x : col, row)] != null)
                            {
                                if (discrepantRow == -1)
                                {
                                    discrepantRow = row;
                                }
                                else
                                {
                                    goto nextColumn;
                                }
                            }
                            else if (state[coord(col, row)] == null || state[coord(col, row)].Value % 2 != state[coord(x, row)].Value % 2)
                            {
                                goto nextColumn;
                            }
                        }
                        for (var v = state.MinValue; v <= state.MaxValue; v++)
                        {
                            if (v % 2 == state[coord(numUnknownsInColumn == 0 ? x : col, discrepantRow)].Value % 2)
                            {
                                state.MarkImpossible(coord(numUnknownsInColumn == 0 ? col : x, discrepantRow), v);
                            }
                        }
                        nextColumn :;
                    }
                }

                var numUnknownsInRow = Enumerable.Range(0, SideLength).Count(col => state[coord(col, y)] == null);
                if (numUnknownsInRow < 2)
                {
                    // See comment above for columns
                    for (var row = 0; row < SideLength; row++)
                    {
                        if (row == y)
                        {
                            continue;
                        }

                        var discrepantCol = -1;
                        for (var col = 0; col < SideLength; col++)
                        {
                            if (state[coord(col, numUnknownsInRow == 0 ? row : y)] == null && state[coord(col, numUnknownsInRow == 0 ? y : row)] != null)
                            {
                                if (discrepantCol == -1)
                                {
                                    discrepantCol = col;
                                }
                                else
                                {
                                    goto nextRow;
                                }
                            }
                            else if (state[coord(col, row)] == null || state[coord(col, row)].Value % 2 != state[coord(col, y)].Value % 2)
                            {
                                goto nextRow;
                            }
                        }
                        for (var v = state.MinValue; v <= state.MaxValue; v++)
                        {
                            if (v % 2 == state[coord(discrepantCol, numUnknownsInRow == 0 ? y : row)].Value % 2)
                            {
                                state.MarkImpossible(coord(discrepantCol, numUnknownsInRow == 0 ? row : y), v);
                            }
                        }
                        nextRow :;
                    }
                }
            }
            return(null);
        }
Пример #20
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            int req;

            // Do we know the parity yet?
            switch (Type)
            {
            case OddEvenType.AllEven:
                req = 0;
                break;

            case OddEvenType.AllOdd:
                req = 1;
                break;

            case OddEvenType.AllSame:
                // Check if any of the affected cells knows its parity
                for (var ix = 0; ix < AffectedCells.Length; ix++)
                {
                    var cell = AffectedCells[ix];
                    if (state[cell] != null)
                    {
                        req = state[cell].Value % 2;
                        goto found;
                    }

                    int?parity = null;
                    for (var value = state.MinValue; value <= state.MaxValue; value++)
                    {
                        if (!state.IsImpossible(cell, value))
                        {
                            if (parity == null)
                            {
                                parity = value % 2;
                            }
                            else if (parity.Value != value % 2)
                            {
                                goto busted;
                            }
                        }
                    }
                    if (parity != null)
                    {
                        req = parity.Value;
                        goto found;
                    }

                    busted :;
                }
                return(null);

            default:
                throw new InvalidOperationException(string.Format(@"OddEvenConstraint.Type has unknown value: {0}", Type));
            }

found:
            // Mark all the cells of the wrong parity as taken. After this, we don’t need the constraint anymore.
            foreach (var cell in AffectedCells)
            {
                state.MarkImpossible(cell, value => value % 2 != req);
            }
            return(ConstraintResult.Remove);
        }
Пример #21
0
        /// <summary>Override; see base.</summary>
        public override ConstraintResult Process(SolverState state)
        {
            if (state.LastPlacedCell != null)
            {
                foreach (var cell in AffectedCells)
                {
                    if (cell != state.LastPlacedCell.Value)
                    {
                        state.MarkImpossible(cell, state.LastPlacedValue);
                    }
                }
            }
            else
            {
                // In case this constraint was returned from another constraint when some of the grid is already filled in, make sure to enforce uniqueness correctly.
                foreach (var cell1 in AffectedCells)
                {
                    if (state[cell1] != null)
                    {
                        foreach (var cell2 in AffectedCells)
                        {
                            if (cell2 != cell1)
                            {
                                state.MarkImpossible(cell2, state[cell1].Value);
                            }
                        }
                    }
                }
            }

            // Special case: if the number of values equals the number of cells, we can detect further optimizations
            if (state.MaxValue - state.MinValue + 1 == AffectedCells.Length)
            {
                if (_optimizationList == null || _optimizationList.Length != state.MaxValue - state.MinValue + 1)
                {
                    _optimizationList = new List <int> [state.MaxValue - state.MinValue + 1];
                }
                var cells = _optimizationList;
                for (var i = 0; i < cells.Length; i++)
                {
                    if (cells[i] != null)
                    {
                        cells[i].Clear();
                    }
                }

                foreach (var cell in AffectedCells)
                {
                    for (var v = state.MinValue; v <= state.MaxValue; v++)
                    {
                        if (!state.IsImpossible(cell, v))
                        {
                            if (cells[v - state.MinValue] == null)
                            {
                                cells[v - state.MinValue] = new List <int>();
                            }
                            cells[v - state.MinValue].Add(cell);
                        }
                    }
                }

                for (var v1 = 0; v1 <= state.MaxValue - state.MinValue; v1++)
                {
                    // Detect if a value can only be in one place
                    if (cells[v1]?.Count == 1)
                    {
                        state.MustBe(cells[v1][0], v1 + state.MinValue);
                    }

                    for (var v2 = v1 + 1; v2 <= state.MaxValue - state.MinValue; v2++)
                    {
                        // Detect if two values can only be in two places (“pair”)
                        if (cells[v1]?.Count == 2 && cells[v2]?.Count == 2 && cells[v1].All(cells[v2].Contains))
                        {
                            foreach (var c in cells[v1])
                            {
                                state.MarkImpossible(c, v => v != v1 + state.MinValue && v != v2 + state.MinValue);
                            }
                        }

                        for (var v3 = v2 + 1; v3 <= state.MaxValue - state.MinValue; v3++)
                        {
                            // Detect if three values can only be in three places (“triplet”)
                            if (cells[v1]?.Count <= 3 && cells[v2]?.Count <= 3 && cells[v3]?.Count <= 3)
                            {
                                var hashSet = new HashSet <int>(cells[v1]);
                                hashSet.AddRange(cells[v2]);
                                hashSet.AddRange(cells[v3]);
                                if (hashSet.Count <= 3)
                                {
                                    foreach (var c in hashSet)
                                    {
                                        state.MarkImpossible(c, v => v != v1 + state.MinValue && v != v2 + state.MinValue && v != v3 + state.MinValue);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return(null);
        }
Пример #22
0
 /// <summary>Override; see base.</summary>
 public override ConstraintResult Process(SolverState state)
 {
     state.MustBe(Cell, Value);
     return(ConstraintResult.Remove);
 }