/// <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); }
/// <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); } } }
/// <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); }
/// <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); }
/// <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); } }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
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); }
/// <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);
/// <summary>Override; see base.</summary> public override ConstraintResult Process(SolverState state) => Lambda(state);
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <summary>Override; see base.</summary> public override ConstraintResult Process(SolverState state) { state.MustBe(Cell, Value); return(ConstraintResult.Remove); }