/// <inheritdoc/> public override void GetAll(IBag <TechniqueInfo> accumulator, IReadOnlyGrid grid) { // Iterate on each region pair. for (int index = 0; index < 18; index++) { // Iterate on each size. var(r1, r2) = (Regions[index, 0], Regions[index, 1]); foreach (var(size, masks) in Combinations) { // Iterate on each combination. foreach (short mask in masks) { var positions = mask.GetAllSets(); var allCellsMap = GridMap.Empty; var pairs = new List <(int, int)>(); foreach (int pos in positions) { int c1 = RegionCells[r1][pos]; int c2 = RegionCells[r2][pos]; allCellsMap.Add(c1); allCellsMap.Add(c2); pairs.Add((c1, c2)); } // Check each pair. // Ensures all pairs should contains same digits // and the kind of digits must be greater than 2. bool checkKindsFlag = true; foreach (var(l, r) in pairs) { if ((grid.GetCandidatesReversal(l) & grid.GetCandidatesReversal(r)).CountSet() < 2) { checkKindsFlag = false; break; } } if (!checkKindsFlag) { // Failed to check. continue; } // Check the mask of cells from two regions. short m1 = 0, m2 = 0; foreach (var(l, r) in pairs) { m1 |= grid.GetCandidatesReversal(l); m2 |= grid.GetCandidatesReversal(r); } int resultMask = m1 | m2; var normalDigits = new List <int>(); var extraDigits = new List <int>(); var digits = resultMask.GetAllSets(); foreach (int digit in digits) { int count = 0; foreach (var(l, r) in pairs) { if (((grid.GetCandidatesReversal(l) & grid.GetCandidatesReversal(r)) >> digit & 1) != 0) { // Both two cells contain same digit. count++; } } if (count >= 2) { // This candidate must be in the structure. normalDigits.Add(digit); } else { // This candidate must be the extra digit. extraDigits.Add(digit); } } if (normalDigits.Count != size) { // The number of normal digits are not enough. continue; } if (resultMask.CountSet() == size + 1) { // Possible type 1 or 2 found. // Now check extra cells. var extraCells = new List <int>(); foreach (int cell in allCellsMap) { if ((grid.GetCandidatesReversal(cell) >> extraDigits[0] & 1) != 0) { extraCells.Add(cell); } } var extraCellsMap = new GridMap(extraCells) & EmptyMap; if (extraCellsMap.IsEmpty) { continue; } // Get all eliminations and highlight candidates. int extraDigit = extraDigits[0]; var conclusions = new List <Conclusion>(); var candidateOffsets = new List <(int, int)>(); if (extraCellsMap.Count == 1) { // Type 1. foreach (int cell in allCellsMap) { if (cell == extraCells[0]) { foreach (int digit in grid.GetCandidatesReversal(cell).GetAllSets()) { if (digit == extraDigit) { continue; } conclusions.Add(new Conclusion(Elimination, cell, digit)); } } else { foreach (int digit in grid.GetCandidatesReversal(cell).GetAllSets()) { candidateOffsets.Add((0, cell * 9 + digit)); } } } if (conclusions.Count == 0) { continue; } accumulator.Add( new XrType1TechniqueInfo( conclusions, views: new[] { new View( cellOffsets: null, candidateOffsets, regionOffsets: null, links: null) }, typeCode: 1, typeName: "Type 1", cells: allCellsMap.ToArray(), digits: normalDigits)); } else { // Type 2. // Check eliminations. var elimMap = new GridMap(extraCells, ProcessPeersWithoutItself); foreach (int cell in elimMap) { if (!(grid.Exists(cell, extraDigit) is true)) { continue; } conclusions.Add(new Conclusion(Elimination, cell, extraDigit)); } if (conclusions.Count == 0) { continue; } // Record all highlight candidates. foreach (int cell in allCellsMap) { foreach (int digit in grid.GetCandidatesReversal(cell).GetAllSets()) { candidateOffsets.Add((digit == extraDigit ? 1 : 0, cell * 9 + digit)); } } accumulator.Add( new XrType2TechniqueInfo( conclusions, views: new[] { new View( cellOffsets: null, candidateOffsets, regionOffsets: null, links: null) }, typeCode: 2, typeName: "Type 2", cells: allCellsMap.ToArray(), digits: normalDigits, extraDigit)); } } else { // Check type 3 or 4. for (int subsetSize = 2; subsetSize <= 8 - size; subsetSize++) { CheckType3Naked( accumulator, grid, allCellsMap, subsetSize, r1, r2, pairs, normalDigits, extraDigits); } CheckType14(accumulator, grid, allCellsMap, normalDigits, extraDigits); } } } } }
/// <summary> /// Check type 1 or 4. /// </summary> /// <param name="accumulator">The accumulator.</param> /// <param name="grid">The grid.</param> /// <param name="allCellsMap">All cells map.</param> /// <param name="normalDigits">The normal digits.</param> /// <param name="extraDigits">The extra digits.</param> private void CheckType14( IBag <TechniqueInfo> accumulator, IReadOnlyGrid grid, GridMap allCellsMap, IReadOnlyList <int> normalDigits, IReadOnlyList <int> extraDigits) { // Now check extra cells. var extraCells = new List <int>(); foreach (int cell in allCellsMap) { foreach (int digit in extraDigits) { if ((grid.GetCandidatesReversal(cell) >> digit & 1) != 0) { extraCells.Add(cell); } } } int extraCellsCount = extraCells.Count; if (extraCellsCount == 1) { // Type 1 found. // Check eliminations. var conclusions = new List <Conclusion>(); int extraCell = extraCells[0]; foreach (int digit in normalDigits) { if (!(grid.Exists(extraCell, digit) is true)) { continue; } conclusions.Add(new Conclusion(Elimination, extraCell, digit)); } if (conclusions.Count == 0) { return; } // Record all highlight candidates. var candidateOffsets = new List <(int, int)>(); foreach (int cell in allCellsMap) { if (cell == extraCell) { continue; } foreach (int digit in grid.GetCandidatesReversal(cell).GetAllSets()) { candidateOffsets.Add((0, cell * 9 + digit)); } } accumulator.Add( new XrType1TechniqueInfo( conclusions, views: new[] { new View( cellOffsets: null, candidateOffsets, regionOffsets: null, links: null) }, typeCode: 1, typeName: "Type 1", cells: allCellsMap.ToArray(), digits: normalDigits)); } else { // TODO: Check XR type 4. } }