/// <summary>
        /// Search for floors.
        /// </summary>
        /// <param name="result">The result accumulator.</param>
        /// <param name="grid">The grid.</param>
        private void SearchForFloors(IBag <TechniqueInfo> result, IReadOnlyGrid grid)
        {
            var series = (Span <int>) stackalloc int[27];

            for (int i = 0; i < 3; i++)
            {
                // Initialize.
                for (int o = 0; o < 27; o++)
                {
                    // All elements in 'series' is in range 0 to 9,
                    // where 0 is for empty cell.
                    series[o] = grid[i * 27 + o] + 1;
                }

                // Get all partial solutions.
                var solutions = new List <int[]>();
                GetAllPartialSolutionsForFloorsRecursively(series.ToArray(), solutions, grid, i, 0);

                // Set all possible candidates.
                var bitmap = new BitArray(27 * 9);
                foreach (int[] solution in solutions)
                {
                    for (int index = 0; index < 27; index++)
                    {
                        int digit = solution[index] - 1;
                        bitmap[index * 9 + digit] = true;
                    }
                }

                // Record all eliminations.
                var conclusions = new List <Conclusion>();
                int z           = 0;
                foreach (bool?v in bitmap)
                {
                    int cell  = i * 27 + z / 9;
                    int digit = z % 9;
                    if (!(v ?? true) && grid.Exists(cell, digit) is true)
                    {
                        conclusions.Add(new Conclusion(ConclusionType.Elimination, cell, digit));
                    }

                    z++;
                }

                if (conclusions.Count == 0)
                {
                    goto Label_GC;
                }

                result.Add(
                    new CccTechniqueInfo(
                        conclusions,
                        views: new[]
示例#2
0
        partial void CheckType1(
            IList <UrTechniqueInfo> accumulator, IReadOnlyGrid grid, int[] urCells, bool arMode,
            short comparer, int d1, int d2, int cornerCell, GridMap otherCellsMap)
        {
            //   ↓ cornerCell
            // (abc) ab
            //  ab   ab

            // Get the summary mask.
            short mask = 0;

            foreach (int cell in otherCellsMap)
            {
                mask |= grid.GetCandidatesReversal(cell);
            }

            if (mask != comparer)
            {
                return;
            }

            // Type 1 found. Now check elimination.
            var conclusions = new List <Conclusion>();

            if (grid.Exists(cornerCell, d1) is true)
            {
                conclusions.Add(new Conclusion(Elimination, cornerCell, d1));
            }
            if (grid.Exists(cornerCell, d2) is true)
            {
                conclusions.Add(new Conclusion(Elimination, cornerCell, d2));
            }
            if (conclusions.Count == 0)
            {
                return;
            }

            var candidateOffsets = new List <(int, int)>();

            foreach (int cell in otherCellsMap)
            {
                foreach (int digit in grid.GetCandidatesReversal(cell).GetAllSets())
                {
                    candidateOffsets.Add((0, cell * 9 + digit));
                }
            }
            if (!_allowIncompletedUr && (candidateOffsets.Count != 6 || conclusions.Count != 2))
            {
                return;
            }

            accumulator.Add(
                new UrType1TechniqueInfo(
                    conclusions,
                    views: new[]
            {
                new View(
                    cellOffsets: arMode ? GetHighlightCells(urCells) : null,
                    candidateOffsets: arMode ? null : candidateOffsets,
                    regionOffsets: null,
                    links: null)
            },
                    digit1: d1,
                    digit2: d2,
                    cells: urCells,
                    isAr: arMode));
        }
示例#3
0
        /// <inheritdoc/>
        public override void GetAll(IBag <TechniqueInfo> accumulator, IReadOnlyGrid grid)
        {
            for (int digit = 0; digit < 9; digit++)
            {
                for (int block = 0; block < 9; block++)
                {
                    // Check the empty rectangle occupies more than 2 cells.
                    // and the structure forms an empty rectangle.
                    var erMap = CandMaps[digit] & RegionMaps[block];
                    if (erMap.Count < 2 || !IsEmptyRectangle(erMap, block, out int row, out int column))
                    {
                        continue;
                    }

                    // Search for conjugate pair.
                    for (int i = 0; i < 12; i++)
                    {
                        var linkMap = CandMaps[digit] & RegionMaps[LinkIds[block, i]];
                        if (linkMap.Count != 2)
                        {
                            continue;
                        }

                        if (linkMap.BlockMask.IsPowerOfTwo() ||
                            i < 6 && (linkMap & RegionMaps[column]).IsEmpty ||
                            i >= 6 && (linkMap & RegionMaps[row]).IsEmpty)
                        {
                            continue;
                        }

                        int[] t           = (linkMap - (i < 6 ? RegionMaps[column] : RegionMaps[row])).ToArray();
                        int   elimRegion  = i < 6 ? t[0] % 9 + 18 : t[0] / 9 + 9;
                        var   elimCellMap = i < 6
                                                        ? CandMaps[digit] & RegionMaps[elimRegion] & RegionMaps[row]
                                                        : CandMaps[digit] & RegionMaps[elimRegion] & RegionMaps[column];

                        if (elimCellMap.IsEmpty)
                        {
                            continue;
                        }

                        int elimCell = elimCellMap.SetAt(0);
                        if (!(grid.Exists(elimCell, digit) is true))
                        {
                            continue;
                        }

                        // Record all highlight candidates.
                        var candidateOffsets = new List <(int, int)>();
                        var cpCells          = new List <int>(2);
                        foreach (int cell in RegionMaps[block] & CandMaps[digit])
                        {
                            candidateOffsets.Add((1, cell * 9 + digit));
                        }
                        foreach (int cell in linkMap)
                        {
                            candidateOffsets.Add((0, cell * 9 + digit));
                            cpCells.Add(cell);
                        }

                        // Empty rectangle.
                        accumulator.Add(
                            new EmptyRectangleTechniqueInfo(
                                conclusions: new[] { new Conclusion(Elimination, elimCell, digit) },
                                views: new[]
示例#4
0
        /// <inheritdoc/>
        public override void GetAll(IBag <TechniqueInfo> accumulator, IReadOnlyGrid grid)
        {
            var house = (Span <int>) stackalloc int[2];
            var alses = Als.GetAllAlses(grid).ToArray();

            for (int i = 0, length = alses.Length; i < length - 1; i++)
            {
                var als1 = alses[i];
                var(isBivalue1, region1, mask1, map1, possibleElimMap1, _) = als1;
                for (int j = i + 1; j < length; j++)
                {
                    var als2 = alses[j];
                    var(isBivalue2, region2, mask2, map2, possibleElimMap2, _) = als2;
                    short xzMask     = (short)(mask1 & mask2);
                    var   map        = map1 | map2;
                    var   overlapMap = map1 & map2;
                    if (!_allowOverlapping && overlapMap.IsNotEmpty)
                    {
                        continue;
                    }

                    // Remove all digits to satisfy that the RCC digit cannot appear
                    // in the intersection of two ALSes.
                    if (overlapMap.IsNotEmpty)
                    {
                        foreach (int cell in overlapMap)
                        {
                            xzMask &= (short)~grid.GetCandidatesReversal(cell);
                        }
                    }

                    // If the number of digits that both two ALSes contain is only one (or zero),
                    // the ALS-XZ will not be formed.
                    if (xzMask.CountSet() < 2 || map.AllSetsAreInOneRegion(out int region))
                    {
                        continue;
                    }

                    short rccMask = 0, z = 0;
                    int   nh = 0;
                    foreach (int digit in xzMask.GetAllSets())
                    {
                        if ((map & CandMaps[digit]).AllSetsAreInOneRegion(out region))
                        {
                            // 'digit' is the RCC digit.
                            rccMask    |= (short)(1 << digit);
                            house[nh++] = region;
                        }
                        else
                        {
                            // 'digit' is the eliminating digit.
                            z |= (short)(1 << digit);
                        }
                    }

                    if (rccMask == 0 || rccMask.IsPowerOfTwo() && z == 0)
                    {
                        continue;
                    }

                    // Check basic eliminations.
                    bool? isDoublyLinked = false;
                    short finalZ         = 0;
                    var   conclusions    = new List <Conclusion>();
                    foreach (int elimDigit in z.GetAllSets())
                    {
                        var elimMap = (CandMaps[elimDigit] & map).PeerIntersection & CandMaps[elimDigit];
                        if (elimMap.IsEmpty)
                        {
                            continue;
                        }

                        foreach (int cell in elimMap)
                        {
                            conclusions.Add(new Conclusion(Elimination, cell, elimDigit));
                        }

                        finalZ |= (short)(1 << elimDigit);
                    }

                    if (_allowAlsCycles && rccMask.CountSet() == 2)
                    {
                        // Doubly linked ALS-XZ.
                        isDoublyLinked = true;
                        foreach (int elimDigit in (z & ~rccMask).GetAllSets())
                        {
                            var zMap = CandMaps[elimDigit] & map1;
                            if (zMap.IsEmpty)
                            {
                                continue;
                            }

                            var elimMap = zMap.PeerIntersection & CandMaps[elimDigit] & map2;
                            if (elimMap.IsEmpty)
                            {
                                continue;
                            }

                            finalZ |= (short)(1 << elimDigit);
                        }

                        // RCC digit 2 eliminations.
                        int k = 0;
                        foreach (int digit in rccMask.GetAllSets())
                        {
                            var elimMap = (RegionMaps[house[k]] & CandMaps[digit]) - map;
                            if (elimMap.IsNotEmpty)
                            {
                                foreach (int cell in elimMap)
                                {
                                    if (grid.Exists(cell, digit) is true)
                                    {
                                        conclusions.Add(new Conclusion(Elimination, cell, digit));
                                    }
                                }
                            }

                            k++;
                        }

                        // Possible eliminations.
                        var tempMap = map;
                        tempMap = CandMaps[mask1.FindFirstSet()];
                        for (k = 1; k < mask1.CountSet(); k++)
                        {
                            tempMap |= CandMaps[mask1.SetAt(k)];
                        }
                        tempMap &= possibleElimMap1;
                        if (tempMap.IsNotEmpty)
                        {
                            foreach (int cell in tempMap)
                            {
                                foreach (int digit in
                                         (grid.GetCandidatesReversal(cell) & (mask1 & ~rccMask)).GetAllSets())
                                {
                                    conclusions.Add(new Conclusion(Elimination, cell, digit));
                                }
                            }
                        }
                        tempMap = CandMaps[mask2.FindFirstSet()];
                        for (k = 1; k < mask2.CountSet(); k++)
                        {
                            tempMap |= CandMaps[mask2.SetAt(k)];
                        }
                        tempMap &= possibleElimMap2;
                        if (tempMap.IsNotEmpty)
                        {
                            foreach (int cell in tempMap)
                            {
                                foreach (int digit in
                                         (grid.GetCandidatesReversal(cell) & (mask2 & ~rccMask)).GetAllSets())
                                {
                                    conclusions.Add(new Conclusion(Elimination, cell, digit));
                                }
                            }
                        }
                    }

                    if (conclusions.Count == 0)
                    {
                        continue;
                    }

                    // Now record highlight elements.
                    bool isEsp            = als1.IsBivalueCell || als2.IsBivalueCell;
                    var  candidateOffsets = new List <(int, int)>();
                    var  cellOffsets      = new List <(int, int)>();
                    if (isEsp)
                    {
                        foreach (int cell in map)
                        {
                            foreach (int digit in grid.GetCandidatesReversal(cell).GetAllSets())
                            {
                                candidateOffsets.Add((finalZ >> digit & 1, cell * 9 + digit));
                            }
                        }
                    }
                    else
                    {
                        foreach (int cell in map1)
                        {
                            short mask             = grid.GetCandidatesReversal(cell);
                            short alsDigitsMask    = (short)(mask & ~(finalZ | rccMask));
                            short targetDigitsMask = (short)(mask & finalZ);
                            short rccDigitsMask    = (short)(mask & rccMask);
                            foreach (int digit in alsDigitsMask.GetAllSets())
                            {
                                candidateOffsets.Add((-1, cell * 9 + digit));
                            }
                            foreach (int digit in targetDigitsMask.GetAllSets())
                            {
                                candidateOffsets.Add((2, cell * 9 + digit));
                            }
                            foreach (int digit in rccDigitsMask.GetAllSets())
                            {
                                candidateOffsets.Add((1, cell * 9 + digit));
                            }
                        }
                        foreach (int cell in map2)
                        {
                            short mask             = grid.GetCandidatesReversal(cell);
                            short alsDigitsMask    = (short)(mask & ~(finalZ | rccMask));
                            short targetDigitsMask = (short)(mask & finalZ);
                            short rccDigitsMask    = (short)(mask & rccMask);
                            foreach (int digit in alsDigitsMask.GetAllSets())
                            {
                                candidateOffsets.Add((-2, cell * 9 + digit));
                            }
                            foreach (int digit in targetDigitsMask.GetAllSets())
                            {
                                candidateOffsets.Add((2, cell * 9 + digit));
                            }
                            foreach (int digit in rccDigitsMask.GetAllSets())
                            {
                                candidateOffsets.Add((1, cell * 9 + digit));
                            }
                        }
                    }

                    accumulator.Add(
                        new AlsXzTechniqueInfo(
                            conclusions,
                            views: new[]
        /// <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.
            }
        }