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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }
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); }