示例#1
0
        public override bool FindPattern(GameBoard board)
        {
            Board = board;
            Cell solutionCell = Board
                                .GetAllLines(Coord.ROWS)
                                .SelectMany(t => t)
                                .FirstOrDefault(cell => cell.PencilMarkCount == 1);

            if (solutionCell == null)
            {
                return(false);
            }

            int pmMask = solutionCell.PMSignature;
            int pm     = pmMask.ListOfBits()[0];

            Solution = new SolveInfo();
            Solution.AddAction(solutionCell, CellRole.Pattern,
                               new PMFinding(pm, PMRole.Solution));

            solutionCell
            .ShadowedCells
            .Where(c => c.IsPencilmarkSet(pm))
            .ToList()
            .ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                GetPmFindings(cell, pmMask, PMRole.Remove)));


            string csvPattern = CellsInCsvFormat(solutionCell);

            Solution.Description = $"Only possible value for cell {csvPattern} is {pm}.";

            return(true);
        }
示例#2
0
        private bool FindXYWing(Cell cell1, Cell cell2)
        {
            Cell       Pincer1           = cell1;
            Cell       Pincer2           = cell2;
            int        commonPMSignature = (cell1.PMSignature & cell2.PMSignature);
            List <int> commonPMs         = commonPMSignature.ListOfBits();
            int        pivotSignature    = cell1.PMSignature ^ cell2.PMSignature;

            if (cell1 == cell2 || commonPMs.Count != 1 || pivotSignature.NumberOfBitsSet() != 2)
            {
                return(false);
            }

            IEnumerable <Cell> shadowedZone = cell1.ShadowedCells
                                              .Intersect(cell2.ShadowedCells);

            affectedCells = shadowedZone.Where(c => (c.PMSignature & commonPMSignature) != 0).ToList();
            if (affectedCells.Count == 0)
            {
                return(false);
            }

            Cell Pivot       = null;
            bool returnValue =
                (cell1.PMSignature | cell2.PMSignature).NumberOfBitsSet() == 3 &&
                cell1.ColIndex != cell2.ColIndex && cell1.RowIndex != cell2.RowIndex &&
                cell1.GroupIndex != cell2.GroupIndex &&
                (Pivot = shadowedZone.FirstOrDefault(c => c.PMSignature == pivotSignature)) != null;

            patternCells = new List <Cell> {
                Pincer1, Pivot, Pincer2
            };
            if (returnValue)
            {
                Solution = new SolveInfo();
                new List <Cell> {
                    Pincer1, Pincer2
                }
                .ForEach(cell => Solution.AddAction(cell, CellRole.Pattern2,
                                                    GetPmFindings(cell, commonPMSignature, PMRole.Pattern)));

                Solution.AddAction(Pivot, CellRole.Pattern, GetPmFindings(Pivot, pivotSignature, PMRole.Pattern));

                affectedCells.ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                                 GetPmFindings(cell, commonPMSignature, PMRole.Remove)));

                string     csvPivot    = CellsInCsvFormat(Pivot);
                string     csvAffected = CellsInCsvFormat(affectedCells);
                string     csvPincers  = CellsInCsvFormat(Pincer1, Pincer2);
                List <int> pivotPMs    = pivotSignature.ListOfBits().ToList();
                Solution.Description = $"XY-Wing Rule:  No matter what the value of cell {csvPivot} is " +
                                       $"(either {pivotPMs[0]} or {pivotPMs[1]}), cell {csvAffected} cannot possibly have value {csvPincers} " +
                                       $"because one of cells {csvPincers} will have that value.";
            }

            return(returnValue);
        }
        public override bool FindPattern(GameBoard board)
        {
            Board = board;
            for (int coord = Coord.ROW; coord <= Coord.SECTION; coord++)
            {
                List <List <Cell> > allLines = board.GetAllLines(coord);
                bool inSections = coord == Coord.SECTION;
                int  secCoord   = inSections ? Coord.ROW : 1 - coord;
                int  thirdCoord = inSections ? Coord.COL : Coord.SECTION;
                for (int iValue = 1; iValue <= board.Dimension; iValue++)
                {
                    Solution = new SolveInfo();
                    List <Cell> singleLine =
                        allLines.FirstOrDefault(line => HasLockedCells(line, iValue, thirdCoord) ||
                                                (coord == Coord.SECTION && HasLockedCells(line, iValue, secCoord)));

                    if (singleLine != null)
                    {
                        int    pmMask             = 1 << iValue;
                        string csvCells           = CellsInCsvFormat(patternCells);
                        string csvAffected        = CellsInCsvFormat(affectedCells);
                        int    involvedCoordinate = coord;
                        int    lineIndex          = singleLine[0].GetCoordinate(coord);

                        affectedCells.ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                                         GetPmFindings(cell, pmMask, PMRole.Remove)));

                        patternCells.ForEach(cell => Solution.AddAction(cell, RuleData.CellRole.Pattern,
                                                                        GetPmFindings(cell, pmMask, PMRole.Pattern)));

                        Board.GetLine(involvedCoordinate, lineIndex)
                        .Except(patternCells)
                        .ToList()
                        .ForEach(cell => Solution.AddAction(cell, RuleData.CellRole.InvolvedLine));

                        Solution.Description = string.Format("Locked Cells Rule: One of cells {0} must have " +
                                                             "solution value {3}.  All cells share a {1} " +
                                                             " and a {2}. Therefore no other cell in that {2} could possibly have that " +
                                                             "value (or else no cell in the {1} could have the value). " +
                                                             "This means value {3} can be ruled out for cells {4}.",
                                                             csvCells, LineNames[coord], LineNames[thirdCoord], iValue, csvAffected);

                        return(true);
                    }
                }
            }

            return(false);
        }
示例#4
0
        public bool IsPathWithRemotePair(List <Cell> path)
        {
            var orderedPath = path.Take(1).ToList();

            path.RemoveAt(0);
            Cell        lastCell      = orderedPath[0];
            Cell        firstCell     = lastCell;
            List <Cell> affectedCells = null;
            bool        solved        = false;
            int         pmMask        = firstCell.PMSignature;

            while (path.Count > 0)
            {
                Cell nextCell = path.FirstOrDefault(t => lastCell.ShadowedCells.Contains(t));
                if (nextCell == null)
                {
                    return(false);
                }

                lastCell = nextCell;
                orderedPath.Add(nextCell);
                path.Remove(nextCell);
                if ((orderedPath.Count & 0x01) == 0x01)
                {
                    continue;
                }

                affectedCells = firstCell
                                .ShadowedCells
                                .Intersect(lastCell.ShadowedCells)
                                .Where(t => (t.PMSignature & pmMask) != 0)
                                .ToList();

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

                solved = true;
                break;
            }

            if (solved)
            {
                Solution = new SolveInfo();
                PMRole[]   chainRoles = new[] { PMRole.ChainColor1, PMRole.ChainColor2 };
                int        linkIndex  = 0;
                List <int> optionPair = pmMask.ListOfBits();
                foreach (Cell pathCell in orderedPath)
                {
                    Solution.AddAction(pathCell, CellRole.Pattern,
                                       new PMFinding(optionPair[0], chainRoles[linkIndex]),
                                       new PMFinding(optionPair[1], chainRoles[linkIndex]));

                    linkIndex = 1 - linkIndex;
                }

                affectedCells.ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                                 GetPmFindings(cell, pmMask, PMRole.Remove)));

                string csvCells    = CellsInCsvFormat(orderedPath, false);
                string csvAffected = CellsInCsvFormat(affectedCells);
                Solution.Description = $"Remote Pair Rule: cells {csvCells} can only have values " +
                                       $"{optionPair[0]} or {optionPair[1]} and form a chain with " +
                                       $"an even number of cells.  Therefore the first and last cells in the chain will " +
                                       $"contain both values, and cells csvAffected (seen by these 2) could not possible have any " +
                                       $"of the 2 values.";
            }

            return(solved);
        }
示例#5
0
        public override bool FindPattern(GameBoard board)
        {
            Board    = board;
            AllCells = Board.GetAllLines(Coord.ROWS).SelectMany(t => t).ToList();
            for (int coord = Coord.ROWS; coord <= Coord.COLUMNS; coord++)
            {
                int secCoord = 1 - coord;
                for (int iValue = 1; iValue <= Board.Dimension; iValue++)
                {
                    int count;

                    // IGrouping.  Key is main coordinate, list is list of 2nd coordinates
                    List <IGrouping <int, int> > lineInfo = AllCells
                                                            .Where(cell => cell.IsPencilmarkSet(iValue))
                                                            .GroupBy(cell => cell.GetCoordinate(coord), cell => cell.GetCoordinate(secCoord))
                                                            .Where(g => (count = g.Count()) >= 2 && count <= MaxCellsPerLine)
                                                            .ToList();

                    if (lineInfo.Count < FishSize)
                    {
                        continue;
                    }

                    var selectedLines    = new List <int>();
                    var selectedSecLines = new List <int>();
                    var selectedFinCells = new List <int[]>();
                    var fishCells        = new List <Cell>();
                    if (SelectFish(lineInfo, secCoord, 0 /*start*/, iValue,
                                   selectedLines, selectedSecLines, selectedFinCells, fishCells))
                    {
                        int pmMask = 1 << affectedPencilmark;

                        Solution = new SolveInfo();
                        affectedCells.ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                                         GetPmFindings(cell, pmMask, PMRole.Remove)));

                        patternCells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                        GetPmFindings(cell, pmMask, PMRole.Pattern)));

                        if (FinCells == null)
                        {
                            FinCells = new List <Cell>();
                        }

                        FinCells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern2,
                                                                    GetPmFindings(cell, pmMask, PMRole.Pattern)));

                        List <int> involvedLineIndexes = patternCells
                                                         .Select(cell => cell.GetCoordinate(coord))
                                                         .Distinct()
                                                         .ToList();


                        involvedLineIndexes
                        .Select(index => Board.GetLine(coord, index))
                        .SelectMany(t => t)
                        .Except(patternCells)
                        .Except(FinCells)
                        .ToList()
                        .ForEach(cell => Solution.AddAction(cell, CellRole.InvolvedLine));

                        string csvCells = CellsInCsvFormat(patternCells);
                        Solution.Description = string.Format("{0}{1} on {2} for pencilmark {4}  Cells involved: {3}.",
                                                             Finned ? "Finned " : (Sashimi ? "Sashimi " : string.Empty),
                                                             FishNames[FishSize - 2],
                                                             coord == Coord.ROWS ? "rows" : "columns",
                                                             csvCells,
                                                             iValue);

                        return(true);
                    }
                }
            }

            return(false);
        }
示例#6
0
        public override bool FindPattern(GameBoard board)
        {
            Board = board;
            InitCellLineGroups();
            int dim = Board.Dimension;

            AllBoardCells = Board.GetAllLines(Coord.ROWS)
                            .SelectMany(t => t)
                            .ToList();

            var         colorizedCells = new List <Cell>();
            List <Cell> affectedCells  = null;

            for (int pm = 1; pm <= dim; pm++)
            {
                Board.ClearPMColorization();
                Logger.Clear();
                GetConjugateSegments(pm);

                while (CellGraph.Count > 0)
                {
                    #region Cleaning for loop 2 and over

                    colorizedCells = Board.GetAllColorizedCells();
                    Logger.WriteLine("Colorized cells are: {0}.", CellsInCsvFormat(colorizedCells, false));
                    StringBuilder finalDescription = new StringBuilder();

                    colorizedCells
                    .Where(cell => VisitedCells.Contains(cell))
                    .ToList()
                    .ForEach(cell => CellGraph.Remove(cell));

                    Logger.WriteLine("Removed colorized from CellGraph.  Remaining cells are {0}.",
                                     CellsInCsvFormat(CellGraph.Keys.ToList()));

                    Board.ClearPMColorization();
                    colorizedCells.Clear();
                    VisitedCells.Clear();

                    #endregion

                    ColorizeSegments(pm);
                    colorizedCells = Board.GetAllColorizedCells();

                    if (colorizedCells.Count < 4)
                    {
                        continue;
                    }

                    if (!MedusaOnly)
                    {
                        #region "Simple Coloring - Rule 2"

                        // Rule 2: If any 2 cell in the same unit have the same color, all such colored
                        // pencilmarks can be eliminated.
                        foreach (var colorValue in new[] { PMColor.COLOR1, PMColor.COLOR2 })
                        {
                            List <Cell> commonColorCells = colorizedCells
                                                           .Where(cell => cell.GetPencilmarkColor(pm) == colorValue)
                                                           .ToList();

                            for (int coord = Coord.ROWS; coord <= Coord.SECTIONS; coord++)
                            {
                                IGrouping <int, Cell> crowdedGroup = commonColorCells
                                                                     .GroupBy(cell => cell.GetCoordinate(coord))
                                                                     .FirstOrDefault(group => group.Count() > 1);

                                if (crowdedGroup != null)
                                {
                                    List <Cell> badColorCells  = commonColorCells;
                                    List <Cell> goodColorCells = colorizedCells
                                                                 .Except(badColorCells)
                                                                 .ToList();

                                    Solution = new SolveInfo();
                                    badColorCells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                                     GetPmFindings(cell, 1 << pm, PMRole.ChainColor2))); // PMRole.Remove

                                    goodColorCells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern2,
                                                                                      GetPmFindings(cell, 1 << pm, PMRole.Pattern)));

                                    string csvAffected = CellsInCsvFormat(badColorCells);
                                    Solution.Description = $"Simple Coloring - Type 1: All sets of cells " +
                                                           $"where a pencilmark appears twice in a row, column or box are found.  Then a " +
                                                           $"graph is made alternating colors.  Once done, any row, column or box having " +
                                                           $"2 or more cells of the same color indicate that the color cannot happen and  " +
                                                           $"should be removed.  Option {pm} will be removed for cells {csvAffected}.";

                                    return(true);
                                }
                            }
                        }

                        #endregion

                        #region "Simple Coloring - Rule 4"

                        // Rule 4:  If any cell in the board, not included in the graph can see
                        // 2 pencilmarks of the same color, then that cell can be removed.
                        do
                        {
                            List <IGrouping <PMColor, Cell> > groupsByColor =
                                colorizedCells.GroupBy(cell => cell.GetPencilmarkColor(pm))
                                .ToList();

                            if (groupsByColor.Count != 2)
                            {
                                continue;
                            }

                            List <Cell> color1Cells = groupsByColor[0].ToList();
                            List <Cell> color2Cells = groupsByColor[1].ToList();

                            affectedCells = AllBoardCells
                                            .Except(colorizedCells)
                                            .Where(cell => cell.IsPencilmarkSet(pm) &&
                                                   cell.GetShadowedCellsInList(color1Cells, pm).Any() &&
                                                   cell.GetShadowedCellsInList(color2Cells, pm).Any())
                                            .ToList();

                            if (affectedCells.Any())
                            {
                                int pmMask = 1 << pm;
                                Solution = new SolveInfo();
                                color1Cells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                               GetPmFindings(cell, pmMask, PMRole.ChainColor1)));

                                color2Cells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                               GetPmFindings(cell, pmMask, PMRole.ChainColor2)));

                                affectedCells.ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                                                 GetPmFindings(cell, pmMask, PMRole.Remove)));

                                string csvAffected = CellsInCsvFormat(affectedCells);
                                string csvChain    = CellsInCsvFormat(colorizedCells.ToList(), false /*sorted*/);
                                Solution.Description = $"Simple Coloring - Type 2: Only 2 cells in a " +
                                                       $"housing with a pencilmark, form a segment.  All segments are joined in a graph, " +
                                                       $"and then cells are colored alternating color.  Any cell in the board which can " +
                                                       $"'see' cells of both colors can be eliminated as a valid option.   Cells {csvAffected} " +
                                                       $"can see cells of both colors in chain {csvChain}.  Pencilmark {pm} can be removed from them.";

                                return(true);
                            }
                        }while (false);

                        #endregion

                        continue;
                    }

                    #region "Medusa Coloring Rules"

                    bool medusaRuleFound = false;
                    ColorizeMedusa(pm);
                    do
                    {
                        #region Medusa - Rule 0: Contradiction found

                        Cell contradictionCell = colorizedCells.FirstOrDefault(cell => cell.ClashedPMColor != PMColor.NONE);
                        if (contradictionCell != null)
                        {
                            string csvCellContradiction = CellsInCsvFormat(contradictionCell);

                            finalDescription.AppendFormat("Medusa contradicted color rule: Following a path of alternating " +
                                                          "colors, led to cell {0} where pencilmark {1} would have both colors.  Therefore color " +
                                                          "{2} is not a solution and can be eliminated.\r\n", csvCellContradiction, contradictionCell.ClashedPM,
                                                          contradictionCell.ClashedPMColor);

                            FalseColorProcessing(colorizedCells, finalDescription, contradictionCell.ClashedPMColor);
                            medusaRuleFound = true;
                            break;
                        }

                        #endregion

                        #region Medusa - Rule 1: PM color twice in a cell

                        colorizedCells = Board.GetAllColorizedCells();
                        PMColor duplicateColor = PMColor.NONE;
                        Cell    dupColorCell   = colorizedCells
                                                 .FirstOrDefault(cell => (duplicateColor = cell.DuplicatePMColor) != PMColor.NONE);

                        if (dupColorCell != null)
                        {
                            List <int> listOfColorPMs = dupColorCell.ListOfColorizedPencilmarks();
                            string     csvColorPMs    = PMsInCsvFormat(listOfColorPMs);
                            string     csvDupCell     = CellsInCsvFormat(dupColorCell);
                            finalDescription.AppendFormat("Rule 1: Cell {0} has pencilmarks {1}, both with color {2}, so the color " +
                                                          "can be eliminated as possible solution in the board.", csvDupCell, csvColorPMs, duplicateColor);

                            FalseColorProcessing(colorizedCells, finalDescription, duplicateColor);
                            medusaRuleFound = true;
                            break;
                        }

                        #endregion

                        #region Medusa - Rule 2: PMs with same value and same color in the same house

                        int len = colorizedCells.Count;
                        for (int iCell = 0; !medusaRuleFound && iCell < len; iCell++)
                        {
                            Cell       colorCell = colorizedCells[iCell];
                            List <int> colorPMs  = colorCell.ListOfColorizedPencilmarks();
                            for (int iCell2 = iCell + 1; !medusaRuleFound && iCell2 < len; iCell2++)
                            {
                                Cell colorCell2 = colorizedCells[iCell2];
                                if (!colorCell2.CanSee(colorCell))
                                {
                                    continue;
                                }

                                foreach (int singleColorPM in colorPMs)
                                {
                                    PMColor pm1Color = colorCell.GetPencilmarkColor(singleColorPM);
                                    PMColor pm2Color = colorCell2.GetPencilmarkColor(singleColorPM);
                                    if (!colorCell2.IsPencilmarkColorized(singleColorPM) ||
                                        pm2Color != colorCell.GetPencilmarkColor(singleColorPM))
                                    {
                                        continue;
                                    }

                                    string csvColorCell    = CellsInCsvFormat(colorCell);
                                    string csvColorCell2   = CellsInCsvFormat(colorCell2);
                                    string commonCoordName = null;
                                    if (colorCell.RowIndex == colorCell2.RowIndex)
                                    {
                                        commonCoordName = "row";
                                    }
                                    else if (colorCell.ColIndex == colorCell2.ColIndex)
                                    {
                                        commonCoordName = "column";
                                    }
                                    else
                                    {
                                        commonCoordName = "box";
                                    }

                                    finalDescription.AppendFormat("Rule 2: Pencilmark {0} has color {1} in cell {2} and also in cell " +
                                                                  "{3}.  The number can only appear once in the {4}.  Therefore this color is the \"false\" color.",
                                                                  singleColorPM, pm1Color, csvColorCell, csvColorCell2, commonCoordName);

                                    FalseColorProcessing(colorizedCells, finalDescription, pm2Color);
                                    medusaRuleFound = true;
                                    break;
                                }
                            }
                        }

                        if (medusaRuleFound)
                        {
                            break;
                        }

                        #endregion

                        #region Medusa - Rule 3: Extra pencilmarks in 2 color cells

                        List <Cell> extraPMCells = colorizedCells
                                                   .Where(cell => cell.PencilMarkCount > 2 && cell.ColorizedPencilmarkCount == 2)
                                                   .ToList();

                        if (extraPMCells.Count > 0)
                        {
                            extraPMCells.ForEach(cell => cell.MarkPencilmarksForRemoval(
                                                     cell.PMUncolorizedSignature
                                                     .ListOfBits()
                                                     .ToArray()));

                            string csvExtraPMs = CellsInCsvFormat(extraPMCells);
                            finalDescription.AppendFormat("Rule 3: Cells {0} include pencilmarks with both colors. Therefore " +
                                                          "Any other pencilmark in these cells can be removed.\r\n", csvExtraPMs);

                            medusaRuleFound = true;
                        }

                        #endregion

                        #region Medusa - Rule 4: Uncolorized pm shadowed by 2 pm's of different colors

                        var Color1CellsByPM = new Dictionary <int, List <Cell> >();
                        var Color2CellsByPM = new Dictionary <int, List <Cell> >();

                        foreach (Cell cell in colorizedCells)
                        {
                            foreach (int colorizedPM in cell.ListOfColorizedPencilmarks())
                            {
                                PMColor pmColor = cell.GetPencilmarkColor(colorizedPM);
                                Dictionary <int, List <Cell> > workDictionary = (pmColor == PMColor.COLOR1) ? Color1CellsByPM : Color2CellsByPM;
                                if (!workDictionary.ContainsKey(colorizedPM))
                                {
                                    workDictionary.Add(colorizedPM, new List <Cell>());
                                }

                                workDictionary[colorizedPM].Add(cell);
                            }
                        }

                        List <int> searchPMs = Color1CellsByPM.Keys.Intersect(Color2CellsByPM.Keys).ToList();
                        affectedCells = new List <Cell>();
                        foreach (int singleSearchPM in searchPMs)
                        {
                            List <Cell> cellsWithPMColor1 = Color1CellsByPM[singleSearchPM];
                            List <Cell> cellsWithPMColor2 = Color2CellsByPM[singleSearchPM];
                            bool        firstItemFound    = false;
                            foreach (Cell cell1 in cellsWithPMColor1)
                            {
                                foreach (Cell cell2 in cellsWithPMColor2)
                                {
                                    if (cell1.CanSee(cell2))
                                    {
                                        continue;
                                    }

                                    List <Cell> solutionCells = cell1.ShadowedCells
                                                                .Intersect(cell2.ShadowedCells)
                                                                .Where(cell => cell.IsPencilmarkSet(singleSearchPM) &&
                                                                       cell.GetPencilmarkColor(singleSearchPM) == PMColor.NONE)
                                                                .ToList();

                                    solutionCells.ForEach(cell => cell.MarkPencilmarksForRemoval(singleSearchPM));
                                    affectedCells.AddRange(solutionCells);

                                    if (solutionCells.Count > 0)
                                    {
                                        if (!firstItemFound)
                                        {
                                            finalDescription.AppendFormat("Rule 4: When a pencilmark is shadowed by 2 colorized " +
                                                                          "pencilmarks in 2 different colors and the same value, the pencilmark is not a solution " +
                                                                          "for the cell.");

                                            firstItemFound  = true;
                                            medusaRuleFound = true;
                                        }

                                        solutionCells.ForEach(cell => finalDescription.AppendFormat("Remove pencilmark {0} from cell {1};",
                                                                                                    singleSearchPM, CellsInCsvFormat(cell)));
                                    }
                                }
                            }
                        }

                        if (affectedCells.Count > 0)
                        {
                            finalDescription.Append("\r\n");
                        }

                        #endregion

                        #region Medusa - Rule 5: Non-Colorized PM sharing colorizedPM in cell can see same value colorizedPM somewhere else

                        // The rule goes like this:  There are 2 candidates in a cell: cA and cB. cB has been colorized "Blue".
                        // If cA can see another cA value PM with color "Green" somewhere else, then cA can be eliminated in the
                        // original cell.  Example:  Cell [5,5] has blue pencilmark for "7", and no color pencilmark "1".   Cell [5,6]
                        // has a green pencilmark "1".  If "Green" is true, then [5, 6] will have a "1" and [5, 5] cannot be "1".
                        // If "blue" is true, then [5, 5] will be "7" filling the value for the cell.   Either way, "1" cannot be an option
                        // for cell [5,5]

                        List <Cell> rule5Candidates = colorizedCells
                                                      .Where(cell => cell.ColorizedPencilmarkCount == 1 && cell.PencilMarkCount >= 2)
                                                      .ToList();

                        affectedCells.Clear();
                        foreach (Cell cand5Cell in rule5Candidates)
                        {
                            PMColor    cellPMColor   = cand5Cell.GetColorOfOnlyColorizedPM();
                            PMColor    searchedColor = cellPMColor.ToggleColor();
                            List <int> noColorPMs    = cand5Cell.PMSignature
                                                       .ListOfBits()
                                                       .Except(cand5Cell.ListOfColorizedPencilmarks())
                                                       .ToList();

                            List <Cell> cellScope = cand5Cell.ShadowedCells.ToList();
                            foreach (int singlePM in noColorPMs)
                            {
                                List <Cell> rule5Cells = cellScope.Where(cell => cell.IsPencilmarkSet(singlePM) &&
                                                                         cell.GetPencilmarkColor(singlePM) == searchedColor)
                                                         .ToList();

                                if (rule5Cells.Count > 0)
                                {
                                    string csvRule5Cells      = CellsInCsvFormat(rule5Cells);
                                    string csvCand5Cell       = CellsInCsvFormat(cand5Cell);
                                    int    colorPMInCand5Cell = cand5Cell.GetPMsWithColor(cellPMColor)[0];
                                    string cellForm           = (rule5Cells.Count > 1) ? "Cells" : "Cell";
                                    string verbForm           = (rule5Cells.Count > 1) ? "have" : "has";

                                    cand5Cell.MarkPencilmarksForRemoval(singlePM);
                                    finalDescription.AppendFormat("Rule 5: {6} {0} {7} pencilmark {1} colorized with {2}. " +
                                                                  "Cell {4} has pencilmark {1} with no color, and pencilmark {5} with color {3}. " +
                                                                  "Therefore, {1} cannot be a solution for cell {4}.\r\n",
                                                                  csvRule5Cells, singlePM, searchedColor, cellPMColor, csvCand5Cell, colorPMInCand5Cell,
                                                                  cellForm, verbForm);

                                    medusaRuleFound = true;
                                }
                            }
                        }
                        #endregion

                        #region Medusa - Rule 6: Cell Emptied by color. All non colored cells see same PM in one color

                        // Rule 6 works like this:   A cell with only non-colorized pencilmarks, where all pencilmarks see pencilmarks
                        // in the same color.  Then, that color is false, or else the cell would be empty.   Example:  Non-colorized
                        // cell has pencilmarks 1, 2, 5.  Cell 1 sees a yellow 1.  2 sees a yellow 2, and 5 sees a yellow 5.  If
                        // yellow assumption was true, then there would be no possible solution for the cell.   Therefore yellow has
                        // to be the "false" color.

                        List <Cell> nonColorizedCells = AllBoardCells
                                                        .Where(cell => cell.Value == 0 && cell.ColorizedPencilmarkCount == 0)
                                                        .ToList();

                        bool foundRule6Case = false;
                        foreach (Cell nonColorCell in nonColorizedCells)
                        {
                            List <int>  nonColorPMList = nonColorCell.PMSignature.ListOfBits();
                            List <Cell> shadowedCells  = nonColorCell.ShadowedCells;
                            bool        complies       = true;
                            foreach (PMColor testColor in new[] { PMColor.COLOR1, PMColor.COLOR2 })
                            {
                                complies = true;
                                foreach (int nonColorPM in nonColorPMList)
                                {
                                    List <Cell> SameColorSamePMList = shadowedCells.Where(cell => cell.IsPencilmarkColorized(nonColorPM) &&
                                                                                          cell.GetPencilmarkColor(nonColorPM) == testColor)
                                                                      .ToList();

                                    if (SameColorSamePMList.Count > 0)
                                    {
                                        continue;
                                    }

                                    complies = false;
                                    break;
                                }

                                if (complies)  // testColor is false
                                {
                                    finalDescription.AppendFormat("Rule 6: Cell {0} has pencilmark(s) {1}, all of which can see " +
                                                                  "a pencilmark with the same value colorized {2}.  This color has to be \"false\" or else the " +
                                                                  " cell would be empty.", CellsInCsvFormat(nonColorCell), PMsInCsvFormat(nonColorPMList),
                                                                  testColor);

                                    FalseColorProcessing(colorizedCells, finalDescription, testColor);
                                    foundRule6Case  = true;
                                    medusaRuleFound = true;
                                    break;
                                }
                            }

                            if (foundRule6Case)
                            {
                                break;
                            }
                        }

                        #endregion
                    } while (false);

                    if (medusaRuleFound)
                    {
                        Solution             = new SolveInfo();
                        Solution.Description = $"3D Medussa Rules\r\n{finalDescription.ToString()}";
                        foreach (Cell cell in colorizedCells)
                        {
                            var pmFindings = new List <PMFinding>();
                            cell.ListOfColorizedPencilmarks()
                            .ForEach(colorPM => pmFindings.Add(
                                         new PMFinding(colorPM, (cell.GetPencilmarkColor(colorPM) == PMColor.COLOR1) ?
                                                       PMRole.ChainColor1 : PMRole.ChainColor2)));

                            cell.ListOfPMsForRemoval.ForEach(removePM => pmFindings.Add(
                                                                 new PMFinding(removePM, PMRole.Remove)));

                            CellRole cellRole = CellRole.Pattern;
                            if (cell.IsPencilmarkColorized(pm) && cell.GetPencilmarkColor(pm) == PMColor.COLOR1)
                            {
                                cellRole = CellRole.Pattern2;
                            }

                            Solution.AddAction(cell, cellRole, pmFindings.ToArray());
                        }

                        AllBoardCells
                        .Except(colorizedCells)
                        .Where(cell => cell.HasPencilmarksMarkedForRemoval)
                        .ToList()
                        .ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                            GetPmFindings(cell, cell.ListOfPMsForRemoval.ToBitMask(), PMRole.Remove)));

                        return(true);
                    }

                    #endregion
                }
            }

            return(false);
        }
示例#7
0
        public override bool FindPattern(GameBoard board)
        {
            Board = board;
            var allALS = new List <AlmostLockedSet>();

            for (int coord = Coord.ROW; coord <= Coord.SECTION; coord++)
            {
                Board.GetAllLines(coord)
                .ForEach(line => allALS.AddRange(
                             AlmostLockedSet.CreateALSsFromLine(line, coord)));
            }

            int      alsCount = allALS.Count;
            ALSMatch match;

            for (int i = 0; i < alsCount; i++)
            {
                for (int j = i + 1; j < alsCount; j++)
                {
                    if ((match = AlmostLockedSet.AreAlsXZ(allALS[i], allALS[j])) == null)
                    {
                        continue;
                    }

                    Solution = new SolveInfo();
                    int commonMask     = 1 << match.commonPencilmark;
                    int restrictedMask = 1 << match.commonRestrictedPencilmark;

                    match.AffectedCells
                    .ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                        GetPmFindings(cell, commonMask, PMRole.Remove)));

                    int pmMask = commonMask | restrictedMask;
                    match.Als1.Cells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                        GetPmFindings(cell,
                                                                                      new int[] { commonMask, restrictedMask },
                                                                                      new PMRole[] { PMRole.ChainColor1, PMRole.ChainColor2 })));

                    match.Als2.Cells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern2,
                                                                        GetPmFindings(cell,
                                                                                      new int[] { commonMask, restrictedMask },
                                                                                      new PMRole[] { PMRole.ChainColor1, PMRole.ChainColor2 })));

                    string csvALS1            = CellsInCsvFormat(match.Als1.Cells);
                    string csvALS2            = CellsInCsvFormat(match.Als2.Cells);
                    string csvAffected        = CellsInCsvFormat(match.AffectedCells);
                    string csvCommonCellsAls1 = CellsInCsvFormat(match.Als1.Cells
                                                                 .Where(cell => cell.IsPencilmarkSet(match.commonPencilmark))
                                                                 .ToList());

                    string csvCommonCellsAls2 = CellsInCsvFormat(
                        match.Als2.Cells
                        .Where(cell => cell.IsPencilmarkSet(match.commonPencilmark))
                        .ToList());

                    int pmCount1 = match.Als1.Cells.Count + 1;
                    int pmCount2 = match.Als2.Cells.Count + 1;

                    Solution.Description = string.Format("ALS - XZ Rule: Cells {0} have a total " +
                                                         "of {1} pencilmarks.  Cells {2} have a total of {3} pencilmarks. " +
                                                         "Pencilmark {4} is common and exclusive to both groups since they all " +
                                                         "can see each other.  Pencilmark {5} will either be in cells {6} or cells {7}." +
                                                         "Since cell(s) {8} are shadowed by all these, then option {5} can be removed " +
                                                         "in these cells.", csvALS1, pmCount1, csvALS2, pmCount2,
                                                         match.commonRestrictedPencilmark, match.commonPencilmark,
                                                         csvCommonCellsAls1, csvCommonCellsAls2, csvAffected);

                    return(true);
                }
            }

            return(false);
        }
        public override bool FindPattern(GameBoard board)
        {
            // The algorithm is the same for finding naked or hidden values.  For naked:
            // an array of integers where each integer is a set of bits for the pencilmarks.
            // (e.g. 01 0110 0010 == 0x162 which mean pencilmark 1, 5, 6, and 8 are set.  The
            // LSB is position 0), and the index in the array is a cell position in the line.
            // For Hidden:  Swap the meaning: The bits are the positions in the line and the
            // index is the pencilmark.
            // Start from the beginning in the array.   Its content is a cumulative mask.
            // If the cumulative mask has more than the goal bits, it is thrown away and the
            // next one is considered.   If N bits are found in a mask using N entries in the
            // array, then there are N of N which is what is being searched for.   Once found,
            // a "final approval method" is invoked to check if the set is good.  The approval
            // is determined to be good if there are pencilmarks to turn off. (for naked, in
            // other positions in the line.  for hidden: more pencilmarks in the same cells
            // (other than the N found (e.g. found 1,2,3 but cell also has 2,7 which can be
            // turned off.  Since there is a large number of combinations the best approach
            // to try promising ones and discard useless ones quickly is to use recursion.
            // Since a board has a small number of cells (9 or so) the recursion depth is not
            // an issue.

            Board = board;
            for (int coord = Coord.ROWS; coord <= Coord.SECTIONS; coord++)
            {
                List <List <Cell> > allLines = Board.GetAllLines(coord);
                foreach (List <Cell> line in allLines)
                {
                    var hiddenFinder = new NakedHiddenFinder();
                    int dim          = line.Count + 1;
                    hiddenFinder.MapBits   = CreateMapBits(line);
                    hiddenFinder.MapLength = hiddenFinder.MapBits.Count;

                    // Step 2:  Search for N of N values
                    int maxGoal = hiddenFinder.MapBits.Count(t => t != 0);
                    if (maxGoal == 0)
                    {
                        continue;
                    }

                    int goal = 1;
                    for (; goal < maxGoal; goal++)
                    {
                        if (hiddenFinder.Search(goal))
                        {
                            break;
                        }
                    }

                    // Finally set the information as related to cells, and information
                    if (goal == maxGoal)
                    {
                        continue;
                    }

                    LineCoordinate = coord;
                    int        indexLine    = line[0].GetCoordinate(LineCoordinate);
                    List <int> locationList = GetLocationList(hiddenFinder);
                    patternCells = locationList.Select(i => line[i]).ToList();

                    PencilmarkList = GetPencilmarkList(hiddenFinder);
                    Solution       = new SolveInfo();
                    int pmMask = GetPencilmarkMask();

                    int patternPMs = 0;
                    patternCells.ForEach(cell => patternPMs |= cell.PMSignature);

                    locationList
                    .Select(loc => line[loc])
                    .ToList()
                    .ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                        GetPmFindings(cell, patternPMs, PMRole.Pattern)));

                    List <int> solutionPMs = pmMask.ListOfBits();
                    CalculateAffectedCells(line);
                    affectedCells.ForEach(affCell =>
                                          Solution.AddAction(affCell, AffectedCellRole,
                                                             GetPmFindings(affCell,
                                                                           new int[] { pmMask, patternPMs },
                                                                           new PMRole[] { PMRole.Remove, PMRole.Pattern })));

                    line
                    .Except(patternCells)
                    .Except(affectedCells)
                    .ToList()
                    .ForEach(cell => Solution.AddAction(cell, CellRole.InvolvedLine));

                    CreateRuleInformation();
                    return(true);
                }
            }

            return(false);
        }
示例#9
0
        private bool MakesUniqueRectangle(List <Cell> firstTwo)
        {
            int    keySignature = firstTwo[0].PMSignature;
            string csvAffected, csvPattern, csvHalf2;
            int    commonCoord;
            int    lineIndex;

            if ((lineIndex = firstTwo[0].RowIndex) == firstTwo[1].RowIndex)
            {
                commonCoord = Coord.ROW;
            }
            else if ((lineIndex = firstTwo[0].ColIndex) == firstTwo[1].ColIndex)
            {
                commonCoord = Coord.COL;
            }
            else
            {
                return(false);
            }

            int        diffCoord   = 1 - commonCoord;
            List <int> cellIndexes = firstTwo
                                     .Select(cell => cell.GetCoordinate(diffCoord))
                                     .ToList();

            List <List <Cell> > workLine = (commonCoord == Coord.ROW) ? allRows : allColumns;
            List <List <Cell> > secondHalfLineCandidates = workLine
                                                           .Where(line => line[0].GetCoordinate(commonCoord) != lineIndex &&
                                                                  (line[cellIndexes[0]].PMSignature & keySignature) == keySignature &&
                                                                  (line[cellIndexes[1]].PMSignature & keySignature) == keySignature)
                                                           .ToList();

            foreach (List <Cell> half2Line in secondHalfLineCandidates)
            {
                int         half2PencilmarkCount = 0;
                List <Cell> half2 = new List <Cell> {
                    half2Line[cellIndexes[0]], half2Line[cellIndexes[1]]
                };
                if (firstTwo.Union(half2)
                    .Select(cell => cell.GetCoordinate(Coord.SECTION))
                    .Distinct()
                    .Count() != 2)
                {
                    continue;
                }

                List <Cell> half2ManyPMs = half2.Where(cell => cell.PencilMarkCount > 2).ToList();
                half2.ForEach(cell => half2PencilmarkCount += cell.PencilMarkCount);
                int pmOtherMask;

                // Unique Rectangle - Type 1
                // 3 cells in the rectangle have 2 options, and the 4th cell has more.
                // In the 4th cell, the 2 common options should be turned off.
                if (half2ManyPMs.Count == 1)
                {
                    Solution = new SolveInfo();
                    Cell solutionCell = half2ManyPMs[0];

                    patternCells = new List <Cell>(firstTwo);
                    patternCells.AddRange(half2.Except(half2ManyPMs).ToList());
                    patternCells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                    GetPmFindings(cell, keySignature, PMRole.Pattern)));

                    affectedCells = half2ManyPMs;
                    Solution.AddAction(solutionCell, CellRole.Pattern,
                                       GetPmFindings(solutionCell, keySignature, PMRole.Remove));

                    patternCells.Add(solutionCell);  // to include in description
                    csvPattern  = CellsInCsvFormat(patternCells);
                    csvAffected = CellsInCsvFormat(affectedCells);
                    string csvValues       = PMsInCsvFormat(keySignature.ListOfBits());
                    string csvSolutionCell = CellsInCsvFormat(solutionCell);
                    Solution.Description = $"Unique Rectangle - Type 1 : Cell {csvAffected} cannot " +
                                           $"have values {csvValues}. Otherwise a deadly pattern would form in cells {csvPattern}. " +
                                           $"Therefore these values can be ruled out for that cell.";

                    return(true);
                }

                // Possible case of Unique Rectangle - Type 2
                // 2 cells have 3 pencilmarks set (the same 3 for both), so one of the
                // 2 cells must be the 3rd value.   No other cells in the line (and section)
                // can hold that value.
                if (half2PencilmarkCount == 6 &&
                    half2[0].PMSignature == half2[1].PMSignature)
                {
                    Solution = new SolveInfo();
                    int mask = half2[0].PMSignature ^ keySignature;
                    affectedCells = half2Line.Except(half2)
                                    .Where(cell => (cell.PMSignature & mask) != 0)
                                    .ToList();

                    if (affectedCells.Count != 0)
                    {
                        List <Cell> affectedInGroup = Board
                                                      .GetLine(Coord.SECTION, half2[0].GetCoordinate(Coord.SECTION))
                                                      .Except(half2)
                                                      .Where(cell => (cell.PMSignature & mask) != 0)
                                                      .ToList();

                        affectedCells.AddRange(affectedInGroup);
                        patternCells = new List <Cell>(firstTwo);
                        patternCells.AddRange(half2);
                        affectedCells
                        .ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                            GetPmFindings(cell, mask, PMRole.Remove)));

                        Board.GetLine(commonCoord, lineIndex)
                        .Except(affectedCells)
                        .ToList()
                        .ForEach(cell => Solution.AddAction(cell, CellRole.InvolvedLine));

                        int iValue = mask.ListOfBits()[0];
                        csvPattern  = CellsInCsvFormat(patternCells);
                        csvAffected = CellsInCsvFormat(affectedCells);
                        csvHalf2    = CellsInCsvFormat(half2);

                        Solution.Description = $"Unique Rectangle - Type 2: Value {iValue} has to " +
                                               $"be present in one of cells {csvHalf2} to avoid a deadly pattern (where the  puzzle " +
                                               $"would have 2 solutions). Therefore, value 2 cannot be present in any of " +
                                               $"cells {csvAffected}.";

                        return(true);
                    }
                }

                // Possible case of Unique Rectangle - Type 3
                // 2 cells have 3 or 4 pencilmarks.  The extra pencilmarks are treated as if
                // they were all in a single cell, and a naked pattern is searched for in the line
                // using these extra values.
                // Example: 4 cells have pencilmarks 3,9 - 3,9 - 1,5,3,9 - 1,5,3,9 .  The extra
                // values (1 and 5) are treated as being in a single cell, and a naked pattern is
                // searched for.  If 2 other cells have (1,4) and (4,5), then the 2 cells and the
                // virtual cell form a naked cell group of size 3 with values 1, 4, 5, and the naked
                // rule is applied accordingly on the line.
                if (half2PencilmarkCount >= 6 && half2PencilmarkCount <= 8)
                {
                    do
                    {
                        int pmsVirtualCell = 0;
                        half2.ForEach(cell => pmsVirtualCell |= cell.PMSignature);
                        pmsVirtualCell ^= firstTwo[0].PMSignature;
                        if (pmsVirtualCell.NumberOfBitsSet() != 2)
                        {
                            continue;
                        }

                        // clear one of the virtual cells completely
                        List <Cell> virtualLine = half2Line.Select(t => t.Clone()).ToList();

                        // change the pencilmarks in the other cell to match the virtual definition
                        virtualLine[cellIndexes[0]].SetValue(0);  // clear pencilmarks
                        virtualLine[cellIndexes[1]].SetValue(0);
                        virtualLine[cellIndexes[1]].SetMultiplePencilMarks(pmsVirtualCell);

                        // Now perform a search for a naked rule set
                        NakedHiddenFinder finder = new NakedHiddenFinder();
                        finder.MapBits   = NakedValuesRule.CreateMapBitsFromLine(virtualLine);
                        finder.MapLength = virtualLine.Count;
                        if (!finder.Search(2 /*goal*/) && !finder.Search(3 /*goal*/))
                        {
                            continue;
                        }

                        int pmsVirtualGroup = finder.ActionIndexes
                                              .Select(i => half2Line[i].PMSignature)
                                              .ToList()
                                              .BitMerge();

                        affectedCells = new List <Cell>();
                        Solution      = new SolveInfo();

                        pmOtherMask   = finder.ActionBits.BitMerge();
                        affectedCells = half2Line
                                        .Except(finder.ActionIndexes.Select(i => half2Line[i]).ToList())
                                        .Except(half2)
                                        .Where(cell => (cell.PMSignature & pmOtherMask) != 0)
                                        .ToList();

                        affectedCells.ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                                         GetPmFindings(cell, pmOtherMask, PMRole.Remove)));

                        int deadlySignature = firstTwo[0].PMSignature;
                        firstTwo.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                    GetPmFindings(cell, deadlySignature, PMRole.Pattern)));

                        half2.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                 GetPmFindings(cell,
                                                                               new int[] { deadlySignature, pmsVirtualCell },
                                                                               new PMRole[] { PMRole.Pattern, PMRole.ChainColor1 })));

                        List <Cell> virtualGroup = finder.ActionIndexes
                                                   .Except(cellIndexes)
                                                   .Select(i => half2Line[i])
                                                   .ToList();

                        virtualGroup.ForEach(cell => Solution.AddAction(cell, CellRole.InvolvedLine,
                                                                        GetPmFindings(cell, pmsVirtualGroup, PMRole.ChainColor1)));

                        List <Cell> deadlyCells = firstTwo.Union(half2).ToList();
                        patternCells = deadlyCells
                                       .Union(finder.ActionIndexes.Select(indexCell => half2Line[indexCell]))
                                       .ToList();

                        List <Cell> line = Board.GetLine(commonCoord, half2[0].GetCoordinate(commonCoord));
                        line
                        .Except(affectedCells)
                        .Except(half2)
                        .Except(virtualGroup)
                        .ToList()
                        .ForEach(cell => Solution.AddAction(cell, CellRole.InvolvedLine));

                        csvAffected = CellsInCsvFormat(affectedCells);
                        csvPattern  = CellsInCsvFormat(patternCells);
                        csvHalf2    = CellsInCsvFormat(half2);
                        string csvDeadly       = CellsInCsvFormat(deadlyCells);
                        string csvValues       = PMsInCsvFormat(pmOtherMask.ListOfBits());
                        string csvPmVirtual    = PMsInCsvFormat(pmsVirtualCell.ListOfBits());
                        string commonCoordName = (commonCoord == Coord.ROW) ? "row" : "column";
                        string csvNakedGroup   = PMsInCsvFormat((pmOtherMask | pmsVirtualCell).ListOfBits());

                        Solution.Description = string.Format("Unique Rectangle Type 3: Cells {0} form a single " +
                                                             "virtual cell with pencilmarks {1}. A deadly pattern in cells {2} cannot take " +
                                                             "place.  One of values {1} has to be present in cells {0} (Naked rule applied " +
                                                             "using virtual cell for pencilmarks {3}) .  Options {5} can be removed from cells " +
                                                             "{6}.", csvHalf2, csvPmVirtual, csvDeadly, csvNakedGroup, commonCoordName,
                                                             csvValues, csvAffected);

                        return(true);
                    }while (false);  // builds a common exit point
                }

                // Last case: Unique Rectangle - Type 4
                // 2 cells have many more pencilmarks set. One of the extra values HAS to be
                // the solution for one of the cells.  Then the 4th cell cannot hold the other
                // value (of the deadly rectangle) or else a deadly pattern would form
                if (half2PencilmarkCount < 6)
                {
                    continue;
                }

                List <Cell> analysisCells = half2Line
                                            .Except(half2)
                                            .ToList();

                List <int> pmAnalysis = keySignature.ListOfBits();
                int        pmWork     = pmAnalysis.FirstOrDefault(pm => analysisCells.FirstOrDefault(
                                                                      cell => cell.IsPencilmarkSet(pm)) == null);

                if (pmWork == 0)
                {
                    continue;
                }

                Solution = new SolveInfo();
                int pmOther = pmAnalysis.FirstOrDefault(t => t != pmWork);
                affectedCells = half2.Where(cell => cell.IsPencilmarkSet(pmOther)).ToList();
                patternCells  = firstTwo.Union(half2).Except(affectedCells).ToList();
                pmOtherMask   = 1 << pmOther;
                int pmWorkMask = 1 << pmWork;

                affectedCells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                 GetPmFindings(cell,
                                                                               new int[] { pmOtherMask, pmWorkMask }, new PMRole[] { PMRole.Remove, PMRole.Pattern })));

                patternCells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                GetPmFindings(cell, pmWorkMask | pmOtherMask, PMRole.Pattern)));

                lineIndex = half2[0].GetCoordinate(commonCoord);
                Board.GetLine(commonCoord, lineIndex)
                .Except(affectedCells)
                .ToList()
                .ForEach(cell => Solution.AddAction(cell, CellRole.InvolvedLine));

                patternCells.AddRange(affectedCells);  // to build description
                csvAffected = CellsInCsvFormat(affectedCells);
                csvPattern  = CellsInCsvFormat(patternCells);
                csvHalf2    = CellsInCsvFormat(half2);
                string lineName = (commonCoord == Coord.ROW) ? "row" : "column";
                Solution.Description = string.Format("Unique Rectangle - Type 4: Either one of cells {0}" +
                                                     "must have value {1}.  If the other cell of the two had value {2}, the 4 cells {3} " +
                                                     "would form a deadly pattern.  Since this cannot happen, cell {4} cannot hold value {2}.",
                                                     csvHalf2, pmWork, pmOther, csvPattern, csvAffected, lineName);

                return(true);
            }

            return(false);
        }
示例#10
0
        public override bool FindPattern(GameBoard board)
        {
            Board = board;
            List <List <Cell> > allGroups = Board.GetAllLines(Coord.SECTION);

            foreach (List <Cell> group in allGroups)
            {
                List <Cell> pivotCandidates = group
                                              .Where(cell => cell.PMSignature.NumberOfBitsSet() == 3)
                                              .ToList();

                int groupIndex = group[0].GetCoordinate(Coord.SECTION);
                foreach (Cell singlePivot in pivotCandidates)
                {
                    List <Cell> pincer1Candidates = group.Where(cell =>
                                                                (cell.PMSignature & singlePivot.PMSignature).NumberOfBitsSet() == 2 &&
                                                                cell.PMSignature.NumberOfBitsSet() == 2)
                                                    .ToList();

                    foreach (Cell pincer1Cand in pincer1Candidates)
                    {
                        int mask = pincer1Cand.PMSignature ^ singlePivot.PMSignature;
                        for (int coord = Coord.ROW; coord <= Coord.COL; coord++)
                        {
                            int lineIndex = singlePivot.GetCoordinate(coord);
                            if (lineIndex == pincer1Cand.GetCoordinate(coord))
                            {
                                continue;
                            }

                            List <Cell> pincer2Candidates = Board.GetLine(coord, lineIndex)
                                                            .Where(cell =>
                                                                   cell.GetCoordinate(Coord.SECTION) != groupIndex &&
                                                                   cell.PMSignature != pincer1Cand.PMSignature &&
                                                                   cell.PencilMarkCount == 2 &&
                                                                   (cell.PMSignature & singlePivot.PMSignature).NumberOfBitsSet() == 2)
                                                            .ToList();

                            foreach (Cell pincer2Cand in pincer2Candidates)
                            {
                                int commonPMSignature = singlePivot.PMSignature ^ pincer2Cand.PMSignature ^ pincer1Cand.PMSignature;
                                int commonPM          = commonPMSignature.ListOfBits()[0];

                                affectedCells = group
                                                .Except(new List <Cell> {
                                    singlePivot
                                })
                                                .Intersect(pincer2Cand.ShadowedCells)
                                                .Where(cell => cell.IsPencilmarkSet(commonPM))
                                                .ToList();

                                if (affectedCells.Any())
                                {
                                    Solution = new SolveInfo();
                                    Solution.AddAction(singlePivot, CellRole.Pattern,
                                                       GetPmFindings(singlePivot, commonPMSignature, PMRole.Pattern));

                                    new List <Cell> {
                                        pincer1Cand, pincer2Cand
                                    }
                                    .ForEach(cell => Solution.AddAction(cell, CellRole.Pattern2,
                                                                        GetPmFindings(cell, commonPMSignature, PMRole.Pattern)));

                                    affectedCells.ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                                                     GetPmFindings(cell, commonPMSignature, PMRole.Remove)));

                                    Board.GetLine(coord, lineIndex)
                                    .Except(new List <Cell> {
                                        singlePivot, pincer1Cand, pincer2Cand
                                    })
                                    .Except(affectedCells)
                                    .ToList()
                                    .ForEach(cell => Solution.AddAction(cell, CellRole.InvolvedLine));

                                    string csvPivot    = CellsInCsvFormat(singlePivot);
                                    string csvPincers  = CellsInCsvFormat(pincer1Cand, pincer2Cand);
                                    string csvAffected = CellsInCsvFormat(affectedCells);

                                    Solution.Description = string.Format("XYZ Wing Rule: Pivot cell {0} with pincer cells {1}. " +
                                                                         "Since 1 of the 3 cells must have common candidate {2}, then cells shadowed by " +
                                                                         "all 3 cells, and having the common candidate {2} ({3}), can have the candidate removed.",
                                                                         csvPivot, csvPincers, commonPM, csvAffected);

                                    return(true);
                                }
                            }
                        }
                    }
                }
            }

            return(false);
        }
示例#11
0
        private bool HasXYChain(Cell cell, int nextPmMask, int lastPM)
        {
            if (Chain.Contains(cell))
            {
                return(false);
            }

            int chainCount = Chain.Count;
            int lastPmMask = 1 << lastPM;

            Chain.Add(cell);
            if (chainCount == 0)
            {
                nextPmMask = cell.PMSignature;
            }

            Logger.WriteLine("Chain is {0}.", CellsInCsvFormat(Chain, false /*sorted*/));

            int  chainLength = Chain.Count;
            Cell chainEnd;

            do
            {
                if (chainLength == 2)
                {
                    int        headSignature = Chain[0].PMSignature;
                    int        pmDiffs       = headSignature ^ cell.PMSignature;
                    List <int> lastPMList    = (pmDiffs & headSignature).ListOfBits();
                    if (lastPMList.Count != 1)
                    {
                        continue;
                    }

                    lastPM     = lastPMList[0];
                    lastPmMask = 1 << lastPM;
                    nextPmMask = pmDiffs & cell.PMSignature;
                }
                else if (chainLength >= 4 && (chainLength & 0x01) == 0 && nextPmMask == lastPmMask)
                {
                    affectedCells = Chain[0].ShadowedCells
                                    .Intersect(cell.ShadowedCells)
                                    .Except(Chain)
                                    .Where(c => c.IsPencilmarkSet(lastPM))
                                    .ToList();

                    if (affectedCells.Count == 0)
                    {
                        affectedCells = null;
                        continue;
                    }

                    Solution = new SolveInfo();
                    affectedCells.ForEach(affCell => Solution.AddAction(affCell, CellRole.Affected,
                                                                        GetPmFindings(affCell, lastPmMask, PMRole.Remove)));

                    int last = Chain.Count - 1;

                    AddSolutionAction(Chain[0], lastPmMask, PMRole.ChainEnd, PMRole.ChainColor2);
                    AddSolutionAction(Chain[last], lastPmMask, PMRole.ChainEnd, PMRole.ChainColor2);

                    PMRole[] linkRoles = new[] { PMRole.ChainColor1, PMRole.ChainColor2 };
                    int      linkIndex = 1;
                    int      lastMask  = lastPmMask;
                    int      nextMask  = Chain[0].PMSignature ^ lastMask;
                    for (int iCell = 1; iCell < last; iCell++)
                    {
                        Cell chainCell   = Chain[iCell];
                        int  currentMask = chainCell.PMSignature ^ nextMask;
                        Solution.AddAction(chainCell, CellRole.Pattern,
                                           GetPmFindings(chainCell,
                                                         new int[] { nextMask, currentMask },
                                                         new PMRole[] { linkRoles[linkIndex], linkRoles[1 - linkIndex] }));

                        nextMask  = currentMask;
                        linkIndex = 1 - linkIndex;
                    }


                    string csvPattern  = CellsInCsvFormat(Chain, false /*sort*/);
                    string csvAffected = CellsInCsvFormat(affectedCells);
                    string csvHead     = CellsInCsvFormat(Chain[0]);
                    string csvLast     = CellsInCsvFormat(cell);

                    Solution.Description = string.Format("XY-Chain Rule:  Cells {0} form a chain, jumping from one to " +
                                                         "the next using a common pencilmark.  One of the ends of the chain (either {1} or {2}) will " +
                                                         "have value {3}. Therefore {4} cannot have value {3}.",
                                                         csvPattern, csvHead, csvLast, lastPM, csvAffected);

                    return(true);
                }

                chainEnd = cell.ShadowedCells
                           .FirstOrDefault(c => c.PMSignature.NumberOfBitsSet() == 2 &&
                                           (c.PMSignature & nextPmMask) != 0 &&
                                           HasXYChain(c, (c.PMSignature ^ nextPmMask), lastPM));

                if (chainEnd != null)
                {
                    return(true);
                }
            } while(false);

            Logger.WriteLine("Trimming Chain to length {0}.", chainCount);
            Chain.Trim(chainCount);
            return(false);
        }