public Cell(int x, int y, CellRole role = CellRole.Empty) { if (x < 0 || y < 0) { throw new Exception("x and y should be greater than 0"); } this._x = x; this._y = y; this._role = role; }
public void AddAction(Cell cell, CellRole cellRole, params PMFinding[] pmFindings) { var finding = new RuleFinding() { Cell = cell, CellRole = cellRole }; finding.PencilmarkDataList.AddRange(pmFindings); Actions.Add(finding); }
public void Highlight(CellRole cellRole, List <PMFinding> pmFindings) { HighlightedPMFindings = pmFindings; BackColor = BoardTraits.dictCellColorMappings[cellRole]; }
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); }