예제 #1
0
 /// <summary>
 /// Initializes an instance with the specified information.
 /// </summary>
 /// <param name="conclusions">All conclusions.</param>
 /// <param name="views">All views.</param>
 /// <param name="als1">The ALS 1.</param>
 /// <param name="als2">The ALS 2.</param>
 /// <param name="bridgeAls">The bridge ALS.</param>
 /// <param name="xDigitsMask">The X digits mask.</param>
 /// <param name="yDigitsMask">The Y digits mask.</param>
 /// <param name="zDigitsMask">The Z digits mask.</param>
 public AlsXyWingTechniqueInfo(
     IReadOnlyList <Conclusion> conclusions, IReadOnlyList <View> views,
     Als als1, Als als2, Als bridgeAls, short xDigitsMask, short yDigitsMask, short zDigitsMask)
     : base(conclusions, views) =>
     (Als1, Als2, BridgeAls, XDigitsMask, YDigitsMask, ZDigitsMask) = (als1, als2, bridgeAls, xDigitsMask, yDigitsMask, zDigitsMask);
        public override void GetAll(IBag <TechniqueInfo> accumulator, IReadOnlyGrid grid)
        {
            var rccs  = new List <(Als _left, Als _right, short _mask)>();
            var alses = Als.GetAllAlses(grid).ToArray();

            // Gather all RCCs.
            for (int i = 0, length = alses.Length; i < length - 1; i++)
            {
                var als1 = alses[i];
                var(_, _, mask1, map1, _, _) = als1;
                for (int j = i + 1; j < length; j++)
                {
                    var als2 = alses[j];
                    var(_, _, mask2, map2, _, _) = als2;
                    var map = map1 | map2;
                    if (map.AllSetsAreInOneRegion(out _) || map1.Overlaps(map2))
                    {
                        continue;
                    }

                    short mask = (short)(mask1 & mask2);
                    if (mask == 0)
                    {
                        continue;
                    }

                    short rccMask = 0;
                    foreach (int digit in mask.GetAllSets())
                    {
                        if ((map & CandMaps[digit]).AllSetsAreInOneRegion(out _))
                        {
                            rccMask |= (short)(1 << digit);
                        }
                    }
                    if (rccMask == 0)
                    {
                        continue;
                    }

                    rccs.Add((als1, als2, rccMask));
                }
            }

            // Now check them.
            for (int i = 0, count = rccs.Count; i < count - 1; i++)
            {
                var(als11, als12, mask1) = rccs[i];
                for (int j = i + 1; j < count; j++)
                {
                    var(als21, als22, mask2) = rccs[j];
                    if (mask1 == mask2 && mask1.IsPowerOfTwo() && mask2.IsPowerOfTwo())
                    {
                        // Cannot form a XY-Wing.
                        continue;
                    }

                    if (!(als11 == als21 ^ als12 == als22 || als11 == als22 ^ als12 == als21))
                    {
                        continue;
                    }

                    // Get the logical order of three ALSes.
                    var(a, b, c) = true switch
                    {
                        _ when als11 == als21 => (als12, als22, als11),
                        _ when als11 == als22 => (als12, als21, als11),
                        _ when als12 == als21 => (als11, als22, als12),
                        _ => (als11, als21, als12)
                    };

                    var(_, aRegion, aMask, aMap, _, _) = a;
                    var(_, bRegion, bMask, bMap, _, _) = b;
                    var(_, cRegion, _, cMap, _, _)     = c;
                    var map = aMap | bMap;
                    if (map == aMap || map == bMap)
                    {
                        continue;
                    }

                    if (!_allowOverlapping && (aMap.Overlaps(bMap) || aMap.Overlaps(cMap) || bMap.Overlaps(cMap)))
                    {
                        continue;
                    }

                    foreach (int digit1 in mask1.GetAllSets())
                    {
                        foreach (int digit2 in mask2.GetAllSets())
                        {
                            if (digit1 == digit2)
                            {
                                continue;
                            }

                            short finalX     = (short)(1 << digit1);
                            short finalY     = (short)(1 << digit2);
                            short digitsMask = (short)(aMask & bMask & ~(finalX | finalY));
                            if (digitsMask == 0)
                            {
                                continue;
                            }

                            // Gather eliminations.
                            short finalZ      = 0;
                            var   conclusions = new List <Conclusion>();
                            foreach (int digit in digitsMask.GetAllSets())
                            {
                                var elimMap = (
                                    ((aMap | bMap) & CandMaps[digit]).PeerIntersection
                                    & CandMaps[digit]) - (aMap | bMap | cMap);
                                if (elimMap.IsEmpty)
                                {
                                    continue;
                                }

                                finalZ |= (short)(1 << digit);
                                foreach (int cell in elimMap)
                                {
                                    conclusions.Add(new Conclusion(Elimination, cell, digit));
                                }
                            }
                            if (conclusions.Count == 0)
                            {
                                continue;
                            }

                            // Record highlight candidates and cells.
                            var cellOffsets      = new List <(int, int)>();
                            var candidateOffsets = new List <(int, int)>();
                            foreach (int cell in aMap)
                            {
                                short mask          = grid.GetCandidatesReversal(cell);
                                short alsDigitsMask = (short)(mask & ~(finalX | finalZ));
                                short xDigitsMask   = (short)(mask & (finalX));
                                short zDigitsMask   = (short)(mask & finalZ);
                                foreach (int digit in alsDigitsMask.GetAllSets())
                                {
                                    candidateOffsets.Add((-1, cell * 9 + digit));
                                }
                                foreach (int digit in xDigitsMask.GetAllSets())
                                {
                                    candidateOffsets.Add((1, cell * 9 + digit));
                                }
                                foreach (int digit in zDigitsMask.GetAllSets())
                                {
                                    candidateOffsets.Add((2, cell * 9 + digit));
                                }
                            }
                            foreach (int cell in bMap)
                            {
                                short mask          = grid.GetCandidatesReversal(cell);
                                short alsDigitsMask = (short)(mask & ~(finalY | finalZ));
                                short yDigitsMask   = (short)(mask & finalY);
                                short zDigitsMask   = (short)(mask & finalZ);
                                foreach (int digit in alsDigitsMask.GetAllSets())
                                {
                                    candidateOffsets.Add((-1, cell * 9 + digit));
                                }
                                foreach (int digit in yDigitsMask.GetAllSets())
                                {
                                    candidateOffsets.Add((1, cell * 9 + digit));
                                }
                                foreach (int digit in zDigitsMask.GetAllSets())
                                {
                                    candidateOffsets.Add((2, cell * 9 + digit));
                                }
                            }
                            foreach (int cell in cMap)
                            {
                                short mask          = grid.GetCandidatesReversal(cell);
                                short alsDigitsMask = (short)(mask & ~(finalX | finalY));
                                short xyDigitsMask  = (short)(mask & (finalX | finalY));
                                foreach (int digit in alsDigitsMask.GetAllSets())
                                {
                                    candidateOffsets.Add((-3, cell * 9 + digit));
                                }
                                foreach (int digit in xyDigitsMask.GetAllSets())
                                {
                                    candidateOffsets.Add((1, cell * 9 + digit));
                                }
                            }

                            accumulator.Add(
                                new AlsXyWingTechniqueInfo(
                                    conclusions,
                                    views: new[]
예제 #3
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[]
예제 #4
0
 /// <summary>
 /// Initializes an instance with the specified information.
 /// </summary>
 /// <param name="conclusions">All conclusions.</param>
 /// <param name="views">All views.</param>
 /// <param name="als1">The ALS 1 used.</param>
 /// <param name="als2">The ALS 2 used.</param>
 /// <param name="xDigitsMask">The X digits mask.</param>
 /// <param name="zDigitsMask">The Z digits mask.</param>
 /// <param name="isDoublyLinked">Indicates whether the instance is a doubly linked ALS-XZ.</param>
 public AlsXzTechniqueInfo(
     IReadOnlyList <Conclusion> conclusions, IReadOnlyList <View> views, Als als1,
     Als als2, short xDigitsMask, short zDigitsMask, bool?isDoublyLinked) : base(conclusions, views) =>
     (Als1, Als2, XDigitsMask, ZDigitsMask, IsDoublyLinked) = (als1, als2, xDigitsMask, zDigitsMask, isDoublyLinked);
예제 #5
0
        /// <inheritdoc/>
        public override void GetAll(IBag <TechniqueInfo> accumulator, IReadOnlyGrid grid)
        {
            var alses = Als.GetAllAlses(grid).ToArray();

            // Gather all conjugate pairs.
            var conjugatePairs = new ICollection <ConjugatePair>?[9];

            for (int digit = 0; digit < 9; digit++)
            {
                for (int region = 0; region < 27; region++)
                {
                    var temp = RegionMaps[region] & CandMaps[digit];
                    if (temp.Count != 2)
                    {
                        continue;
                    }

                    (conjugatePairs[digit] ??= new List <ConjugatePair>()).Add(new ConjugatePair(temp, digit));
                }
            }

            // Iterate on each ALS.
            for (int i = 0, length = alses.Length; i < length - 1; i++)
            {
                var als1 = alses[i];
                var(_, region1, mask1, map1, _, _) = als1;
                for (int j = i + 1; j < length; j++)
                {
                    var als2 = alses[j];
                    var(_, region2, mask2, map2, _, _) = als2;
                    if (map1.Overlaps(map2) || (map1 | map2).AllSetsAreInOneRegion(out _))
                    {
                        continue;
                    }

                    short mask = (short)(mask1 & mask2);
                    if (mask.CountSet() < 2)
                    {
                        continue;
                    }

                    foreach (int x in mask.GetAllSets())
                    {
                        if ((conjugatePairs[x]?.Count ?? 0) == 0)
                        {
                            continue;
                        }

                        var p1 = (map1 & CandMaps[x]).PeerIntersection & CandMaps[x];
                        var p2 = (map2 & CandMaps[x]).PeerIntersection & CandMaps[x];
                        if (p1.IsEmpty || p2.IsEmpty)
                        {
                            // At least one of two ALSes cannot see the node of the conjugate pair.
                            continue;
                        }

                        // Iterate on each conjugate pair.
                        short wDigitsMask = 0;
                        var   conclusions = new List <Conclusion>();
                        foreach (var conjugatePair in conjugatePairs[x] ?? Array.Empty <ConjugatePair>())
                        {
                            var cpMap = conjugatePair.Map;
                            if (cpMap.Overlaps(map1) || cpMap.Overlaps(map2))
                            {
                                // Conjugate pair cannot overlap with the ALS structure.
                                continue;
                            }

                            if ((cpMap & p1).Count != 1 || (cpMap & p2).Count != 1 || ((p1 | p2) & cpMap).Count != 2)
                            {
                                continue;
                            }

                            foreach (int w in (mask & ~(1 << x)).GetAllSets())
                            {
                                var tempMap = ((map1 | map2) & CandMaps[w]).PeerIntersection & CandMaps[w];
                                if (tempMap.IsEmpty)
                                {
                                    continue;
                                }

                                wDigitsMask |= (short)(1 << w);
                                foreach (int cell in tempMap)
                                {
                                    conclusions.Add(new Conclusion(Elimination, cell, w));
                                }
                            }

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

                            // Record highlight cell and candidate offsets.
                            var cellOffsets = new List <(int, int)>();
                            cellOffsets.AddRange(from cell in map1 select(-1, cell));
                            cellOffsets.AddRange(from cell in map2 select(-2, cell));

                            var candidateOffsets = new List <(int, int)>
                            {
                                (0, cpMap.SetAt(0) * 9 + x), (0, cpMap.SetAt(1) * 9 + x)
                            };
                            foreach (int cell in map1)
                            {
                                foreach (int digit in grid.GetCandidatesReversal(cell).GetAllSets())
                                {
                                    candidateOffsets.Add((
                                                             true switch
                                    {
                                        _ when digit == x => 1,
                                        _ when(wDigitsMask >> digit & 1) != 0 => 2,
                                        _ => - 1
                                    },
예제 #6
0
 /// <summary>
 /// Initializes an instance with the specified information.
 /// </summary>
 /// <param name="conclusions">All conclusions.</param>
 /// <param name="views">All views.</param>
 /// <param name="als1">The ALS 1.</param>
 /// <param name="als2">The ALS 2.</param>
 /// <param name="conjugatePair">The conjugate pair.</param>
 /// <param name="wDigitsMask">The W digits mask.</param>
 /// <param name="x">The X digit.</param>
 public AlsWWingTechniqueInfo(
     IReadOnlyList <Conclusion> conclusions, IReadOnlyList <View> views,
     Als als1, Als als2, ConjugatePair conjugatePair, short wDigitsMask, int x) : base(conclusions, views) =>
     (Als1, Als2, ConjugatePair, WDigitsMask, XDigit) = (als1, als2, conjugatePair, wDigitsMask, x);