public Puzzle LoadFromString(string input) { Group[] rows = new Group[9]; try { var enumerator = input.GetEnumerator(); for (int row = 0; row < 9; row++) { var rowCells = new Cell[9]; for (int col = 0; col < 9; col++) { enumerator.MoveNext(); var current = enumerator.Current; rowCells[col] = new Cell(current, row + 1, col + 1, _logger); } rows[row] = new Group(rowCells, $"row {row+1}", _logger); } } catch (IOException e) { _logger.Log("LoadPuzzle", $"Problem reading puzzle data: {e.Message}"); throw; } return(new Puzzle(rows, _logger)); }
public void FillIn(char candidate, string actor) { Filled = true; Value = candidate; Candidates = null; _log.Log(actor, $"Filled in a {candidate} at {Col},{Row}!"); }
internal bool IsValidActivityType(string activityTypeCode, string policyNumber, IMessageLogger log) { bool success = activityTypeCode.Equals(Constants.TransactionType.PENDINGCANCEL, StringComparison.InvariantCultureIgnoreCase) || activityTypeCode.Equals(Constants.BillingActivityTypeCodes.RESCINDPCN, StringComparison.InvariantCultureIgnoreCase); if (!success) { log.Log( new Log() { Messages = new List <LogMessage>() { new LogMessage() { Category = Constants.Logging.CATEGORY, Severity = SeverityType.Information, Message = $"Detected an activity type to not process: {activityTypeCode} for {policyNumber}", } } }, policyNumber); } return(success); }
public bool Apply(Puzzle puzzle) { _searchCombos = 0; bool progress = false; // Iterate each row, column and box, looking for 3 cells that contain only 3 candidates for (int num = 1; num < 10; num++) { var group = puzzle.GetRow(num); progress = CheckGroup(group) || progress; group = puzzle.GetColumn(num); progress = CheckGroup(group) || progress; group = puzzle.GetBox(num); progress = CheckGroup(group) || progress; } _logger.Log(Name, $"Searched {_searchCombos} group/triplet combos"); return(progress); }
public void Post([FromBody] LogMessage message) { if (message == null || string.IsNullOrWhiteSpace(message.Message)) { throw new Exception($"Value for {nameof(message)} is missing."); } _messageLogger.Log(message.Message); }
public IActionResult Post([FromBody] LogMessage message) { if (message == null || string.IsNullOrWhiteSpace(message.Message)) { return(BadRequest($"Value for {nameof(message)} is missing.")); } _messageLogger.Log(message.Message); return(Accepted()); }
private bool CheckGroup(Group group) { bool progress = false; for (int firstCell = 1; firstCell <= 8; firstCell++) { var firstCellData = group.GetCell(firstCell); if (firstCellData.Given || firstCellData.Filled) { continue; } if (firstCellData.Candidates.Count == 2) { // See if any of the other cells are a pair with this one for (int secondCell = firstCell + 1; secondCell <= 9; secondCell++) { var secondCellData = group.GetCell(secondCell); if (secondCellData.Given || secondCellData.Filled) { continue; } if (secondCellData.Candidates.SetEquals(firstCellData.Candidates)) { bool pairProgress = false; // This is a naked pair - remove candidates from all other cells! for (int cellNum = 1; cellNum <= 9; cellNum++) { if (cellNum == firstCell || cellNum == secondCell) { continue; } var cell = group.GetCell(cellNum); if (!cell.Given && !cell.Filled) { if (cell.EliminateCandidates(firstCellData.Candidates, Name)) { pairProgress = true; } } } if (pairProgress) { _logger.Log(Name, $"{firstCellData.Candidates.First()}{firstCellData.Candidates.Skip(1).First()} in {group.Description} helps us"); progress = true; } } } } } return(progress); }
public void WriteGridAsText(IMessageLogger logger) { for (int row = 1; row <= 9; row++) { if ((row - 1) % 3 == 0) { logger.Log(null, "-------------"); } var rowData = GetRow(row); var msg = ""; for (int col = 1; col <= 9; col++) { if ((col - 1) % 3 == 0) { msg += "|"; } msg += rowData.GetCell(col).Value; } logger.Log(null, msg + "|"); } logger.Log(null, "-------------"); }
private bool RemoveCandidatesFromRow(Puzzle puzzle, int rowNum, int rowOffset, int colOffset, char candidate, int boxNum) { bool progress = false; var group = puzzle.GetRow(rowNum + rowOffset); for (int colNum = 1; colNum <= 9; colNum++) { if (colNum > colOffset && colNum <= colOffset + 3) { // Skip the 3 cells in the current box continue; } if (group.GetCell(colNum).EliminateCandidate(candidate, Name)) { progress = true; } } if (progress) { _logger.Log(Name, $"{candidate}s all in a row in box {boxNum}"); } return(progress); }
internal bool IsValid() { var valid = true; var candidates = new HashSet <char>(); var placed = new HashSet <char>(); foreach (var cell in _cells) { if (cell.Filled) { if (placed.Contains(cell.Value)) { _log.Log(null, $"{cell.Value} filled in more than once in {this.Description}!", SudokuBrain.Models.LogItemLevel.Problem); valid = false; } placed.Add(cell.Value); } else { candidates.UnionWith(cell.Candidates); } } placed.UnionWith(candidates); var allNumbers = new HashSet <char>(Constants.AllValues); foreach (var ch in placed) { allNumbers.Remove(ch); } if (allNumbers.Count > 0) { _log.Log(null, $"{allNumbers.First()} is missing from {this.Description}!", SudokuBrain.Models.LogItemLevel.Problem); valid = false; } return(valid); }
internal bool IsValidIssueSystemCode(string issueSystemCode, string policyNumber, IMessageLogger log) { bool success = issueSystemCode.Equals(Constants.IssueSystemCode.DUCKPERSONAL, StringComparison.InvariantCultureIgnoreCase); if (!success) { log.Log( new Log() { Messages = new List <LogMessage>() { new LogMessage() { Category = Constants.Logging.CATEGORY, Severity = SeverityType.Information, Message = $"Detected a system code to not process: {issueSystemCode} for {policyNumber}", } } }, policyNumber); } return(success); }
public void EndSolutionStats(Puzzle puzzle) { _logger.Log(null, "Strategies applied successfully:"); int difficulty = 0; foreach (var strat in puzzle.UsedStrats) { difficulty += strat.Value; _logger.Log(null, $"{strat.Key}"); } if (puzzle.CountFilledInCells == 81) { _logger.Log(null, $"Difficulty: {difficulty}"); _logger.Log(null, "All Done :)"); } else { _logger.Log(null, $"Difficulty: {difficulty}+"); _logger.Log(null, "Ran out of ideas :/", SudokuBrain.Models.LogItemLevel.Problem); } }
public void Log(string message) { _messageLogger.Log(message); }
public async Task <Result> Run() { var templates = _configurationExplorer.Explore(_options.SourcePath, _options.ConfigurationFiles); if (templates.State == Result.Error) { return(Result.Error); } var buildGraphResult = _buildGraphFactory.Create(templates.Value); if (buildGraphResult.State == Result.Error) { return(Result.Error); } var buildGraphsResult = _buildGraphsFactory.Create(buildGraphResult.Value); if (buildGraphsResult.State == Result.Error) { return(Result.Error); } var buildGraphs = buildGraphsResult.Value.ToList(); if (!buildGraphs.Any()) { _logger.Log("Nothing to build.", Result.Error); return(Result.Error); } var dockerFilesRootPath = _fileSystem.UniqueName; var contextStreamResult = await _contextFactory.Create(dockerFilesRootPath, buildGraphResult.Value.Nodes.Select(i => i.Value).OfType <Image>().Select(i => i.File)); if (contextStreamResult.State == Result.Error) { return(Result.Error); } var contextStream = contextStreamResult.Value; using (contextStream) using (_logger.CreateBlock("Build")) { var labels = new Dictionary <string, string>(); foreach (var buildGraph in buildGraphs) { var name = _graphNameFactory.Create(buildGraph).Value ?? "Unnamed graph"; if (!string.IsNullOrWhiteSpace(_options.FilterRegex) && !new Regex(_options.FilterRegex).IsMatch(name)) { _logger.Log($"\"{name}\" was skipped according to filter \"{_options.FilterRegex}\".", Result.Warning); continue; } using (_logger.CreateBlock(name)) { var buildPath = _buildPathProvider.GetPath(buildGraph).ToList(); foreach (var buildNode in buildPath) { switch (buildNode.Value) { case Image image: var dockerFile = image.File; using (_logger.CreateBlock(dockerFile.ToString())) { contextStream.Position = 0; var dockerFilePathInContext = _pathService.Normalize(Path.Combine(dockerFilesRootPath, dockerFile.Path)); var buildParameters = new ImageBuildParameters { Dockerfile = dockerFilePathInContext, Tags = dockerFile.Tags.Distinct().ToList(), Labels = labels }; using (var buildEventStream = await _dockerClient.Images.BuildImageFromDockerfileAsync( contextStream, buildParameters, _cancellationTokenSource.Token)) { _streamService.ProcessLines(buildEventStream, line => { _messageLogger.Log(line); }); } } break; } } } } } return(Result.Success); }
private bool CheckGroup(Group group) { bool progress = false; // Get all remaining candidates in the group var candidates = new HashSet <char>(Constants.AllValues); for (int cell = 1; cell <= 9; cell++) { var cellData = group.GetCell(cell); if (cellData.Given || cellData.Filled) { candidates.Remove(cellData.Value); } } var candidatesList = new List <char>(candidates); if (candidatesList.Count >= 4) { // Iterate all sets of 4 possible candidates int combos = Helpers.Factorial(candidates.Count) / (Helpers.Factorial(4) * Helpers.Factorial(candidates.Count - 4)); _logger.Log(Name, $"Hidden quads: searching in {group.Description} with {combos} quad combos"); for (var i = 0; i < candidatesList.Count - 2; i++) { var candidate1 = candidatesList[i]; for (var j = i + 1; j < candidatesList.Count - 1; j++) { var candidate2 = candidatesList[j]; for (var k = j + 1; k < candidatesList.Count; k++) { var candidate3 = candidatesList[k]; // Find all cells containing 2 or 3 of the candidates var permutation = new char[] { candidate1, candidate2, candidate3 }; var rejected = false; var inCells = new List <int>(); for (int cell = 1; cell <= 9; cell++) { var cellData = group.GetCell(cell); if (cellData.Given || cellData.Filled) { continue; } var foundCandidates = new HashSet <char>(cellData.Candidates); foundCandidates.IntersectWith(permutation); if (foundCandidates.Count == 1) { rejected = true; break; } if (foundCandidates.Count > 3) { throw new Exception("makes no sense"); } if (foundCandidates.Count > 1) { if (inCells.Count >= 3) { // This triplet is in too many cells rejected = true; break; } inCells.Add(cell); } } if (rejected) { continue; } if (inCells.Count == 3) { _logger.Log(Name, $"{candidate1}{candidate2}{candidate3} in {group.Description}"); // We found a hidden triplet! Expose it bool exposeProgress = false; var tripleValues = new char[] { candidate1, candidate2, candidate3 }; foreach (var cellNum in inCells) { var cellData = group.GetCell(cellNum); var currentCandidateCount = cellData.Candidates.Count; cellData.Candidates.IntersectWith(tripleValues); if (cellData.Candidates.Count < currentCandidateCount) { exposeProgress = true; } } if (exposeProgress) { _logger.Log(Name, $"Exposed {candidate1}{candidate2}{candidate3} in {group.Description}", SudokuBrain.Models.LogItemLevel.Debug); progress = true; } bool tripletProgress = false; // This is a naked triplet - remove candidates from all other cells! for (int cellNum = 1; cellNum <= 9; cellNum++) { if (inCells.Contains(cellNum)) { continue; } var cell = group.GetCell(cellNum); if (!cell.Given && !cell.Filled) { if (cell.EliminateCandidates(tripleValues, Name)) { tripletProgress = true; } } } if (tripletProgress) { _logger.Log(Name, $"Progress {candidate1}{candidate2}{candidate3} in {group.Description}"); progress = true; } } } } } } return(progress); }
public bool ApplyToLine(Group line, int lineNum, bool isColumn) { bool progress = false; foreach (var candidate in line.Candidates) { // Check if this digit is a candidate in only 2 or 3 locations var cellNumbers = new HashSet <int>(); for (int cellNum = 1; cellNum <= 9; cellNum++) { var cell = line.GetCell(cellNum); if (!cell.Filled && cell.Candidates.Contains(candidate)) { cellNumbers.Add(cellNum); } } if (cellNumbers.Count == 2 || cellNumbers.Count == 3) { // Check if our candidates are all in the same box number int boxNum = 0; bool valid = true; foreach (var cellNum in cellNumbers) { int currentBoxNum; if (isColumn) { currentBoxNum = Helpers.BoxNumber(lineNum, cellNum); } else { currentBoxNum = Helpers.BoxNumber(cellNum, lineNum); } if (boxNum == 0) { // First cell - record the box number boxNum = currentBoxNum; } else if (boxNum != currentBoxNum) { // Subsequent cell was not in the same box number valid = false; break; } } if (valid) { // Remove this candidate from all the other cells in the box IList <int> skipCells; if (isColumn) { if (lineNum % 3 == 1) { skipCells = Constants.BoxLeftCol; } else if (lineNum % 3 == 2) { skipCells = Constants.BoxMiddleCol; } else { skipCells = Constants.BoxRightCol; } } else { if (lineNum % 3 == 1) { skipCells = Constants.BoxTopRow; } else if (lineNum % 3 == 2) { skipCells = Constants.BoxMiddleRow; } else { skipCells = Constants.BoxBottomRow; } } var box = _puzzle.GetBox(boxNum); bool boxProgress = false; for (int b = 1; b <= 9; b++) { if (!skipCells.Contains(b)) { // This cell isnt in our column/row - remove the candidate var boxCell = box.GetCell(b); if (boxCell.EliminateCandidate(candidate, Name)) { boxProgress = true; } } } if (boxProgress) { progress = true; if (isColumn) { _logger.Log(Name, $"{candidate}s all in a column in box {boxNum}"); } else { _logger.Log(Name, $"{candidate}s all in a row in box {boxNum}"); } } } } } return(progress); }
public async Task <Result> Run() { var templates = _configurationExplorer.Explore(_options.SourcePath, _options.ConfigurationFiles); if (templates.State == Result.Error) { return(Result.Error); } var buildGraphResult = _buildGraphFactory.Create(templates.Value); if (buildGraphResult.State == Result.Error) { return(Result.Error); } var buildGraphsResult = _buildGraphsFactory.Create(buildGraphResult.Value); if (buildGraphsResult.State == Result.Error) { return(Result.Error); } var buildGraphs = buildGraphsResult.Value.ToList(); if (!buildGraphs.Any()) { _logger.Log("Nothing to build.", Result.Error); return(Result.Error); } var dockerFilesRootPath = _fileSystem.UniqueName; var contextStreamResult = await _contextFactory.Create(dockerFilesRootPath, buildGraphResult.Value.Nodes.Select(i => i.Value).OfType <Image>().Select(i => i.File)); if (contextStreamResult.State == Result.Error) { return(Result.Error); } var contextStream = contextStreamResult.Value; using (contextStream) using (_logger.CreateBlock("Build")) { foreach (var buildGraph in buildGraphs) { var nameResult = _nodesDescriptionFactory.Create(buildGraph.Nodes); var name = nameResult.State != Result.Error ? nameResult.Value.Name : "Unnamed graph"; if (!string.IsNullOrWhiteSpace(_options.FilterRegex) && !new Regex(_options.FilterRegex).IsMatch(name)) { _logger.Log($"\"{name}\" was skipped according to filter \"{_options.FilterRegex}\".", Result.Warning); continue; } using (_logger.CreateBlock(name)) { var buildPath = _buildPathProvider.GetPath(buildGraph).ToList(); foreach (var buildNode in buildPath) { switch (buildNode.Value) { case Image image: var dockerFile = image.File; using (_logger.CreateBlock(dockerFile.ToString())) { var id = Guid.NewGuid().ToString(); var labels = new Dictionary <string, string> { { "InternalImageId", id } }; var tags = ( from tag in dockerFile.Tags select $"{dockerFile.ImageId}:{tag}") .Distinct() .ToList(); contextStream.Position = 0; var dockerFilePathInContext = _pathService.Normalize(Path.Combine(dockerFilesRootPath, dockerFile.Path)); var buildParameters = new ImageBuildParameters { Dockerfile = dockerFilePathInContext, Tags = tags, PullParent = true, Labels = labels }; using (var buildEventStream = await _dockerClient.Images.BuildImageFromDockerfileAsync( contextStream, buildParameters, _cancellationTokenSource.Token)) { _streamService.ProcessLines(buildEventStream, line => { _messageLogger.Log(line); }); } var filter = new Dictionary <string, IDictionary <string, bool> > { { "label", labels.ToDictionary(i => $"{i.Key}={i.Value}", _ => true) } }; var images = await _dockerClient.Images.ListImagesAsync(new ImagesListParameters { Filters = filter }); if (images.Count == 0) { _logger.Log($"Error while building the image {dockerFile}", Result.Error); return(Result.Error); } } break; } } } } } return(Result.Success); }
public void Trace(string message, params object[] arguments) { traceLogger.Log(message, arguments); }
public void Error(string message, params object[] arguments) { errorLogger.Log(message, arguments); }
public bool FindSwordfish(Puzzle puzzle, bool columnsSearch) { bool progress = false; var lineCandidatePositions = new Dictionary <char, List <int> > [10]; for (int lineNum = 1; lineNum <= 9; lineNum++) { var candidatePositions = new Dictionary <char, List <int> >(); Group line; if (columnsSearch) { line = puzzle.GetColumn(lineNum); } else { line = puzzle.GetRow(lineNum); } for (int cellNum = 1; cellNum <= 9; cellNum++) { var cell = line.GetCell(cellNum); if (!cell.Filled) { foreach (var candidate in cell.Candidates) { if (candidatePositions.ContainsKey(candidate)) { candidatePositions[candidate].Add(cellNum); } else { candidatePositions[candidate] = new List <int> { cellNum }; } } } } lineCandidatePositions[lineNum] = new Dictionary <char, List <int> >(); foreach (var candidate in candidatePositions.Keys) { if (candidatePositions[candidate].Count == 2 || candidatePositions[candidate].Count == 3) { lineCandidatePositions[lineNum].Add(candidate, candidatePositions[candidate]); } } } // Phew OK we've now got for each line a list of candidates and which 2 or 3 cell numbers they appear in for (int firstLineNum = 1; firstLineNum <= 7; firstLineNum++) { for (int secondLineNum = firstLineNum + 1; secondLineNum <= 8; secondLineNum++) { for (int thirdLineNum = secondLineNum + 1; thirdLineNum <= 9; thirdLineNum++) { var firstLineCandidates = lineCandidatePositions[firstLineNum]; foreach (var candidate in firstLineCandidates.Keys) { var secondLineCandidates = lineCandidatePositions[secondLineNum]; var thirdLineCandidates = lineCandidatePositions[thirdLineNum]; if (secondLineCandidates.ContainsKey(candidate) && thirdLineCandidates.ContainsKey(candidate)) { // Check if the three lines have the candidate within the same 3 positions var allCandidateCells = firstLineCandidates[candidate] .Union(secondLineCandidates[candidate]) .Union(thirdLineCandidates[candidate]) .ToList(); if (allCandidateCells.Count == 3) { // We've found a Swordfish if (columnsSearch) { _logger.Log(Name, $"{candidate} in cols {firstLineNum}, {secondLineNum}, {thirdLineNum} rows {allCandidateCells[0]}, {allCandidateCells[1]}, {allCandidateCells[2]}", LogItemLevel.Debug); } else { _logger.Log(Name, $"{candidate} in rows {firstLineNum}, {secondLineNum}, {thirdLineNum} cols {allCandidateCells[0]}, {allCandidateCells[1]}, {allCandidateCells[2]}", LogItemLevel.Debug); } // Eliminate any other instances of candidate from the 3 perpendicular lines IEnumerable <Group> lines; if (columnsSearch) { lines = allCandidateCells.Select(c => puzzle.GetRow(c)); } else { lines = allCandidateCells.Select(c => puzzle.GetColumn(c)); } var lineProgress = new HashSet <Group>(); for (int cell = 1; cell <= 9; cell++) { if (cell != firstLineNum && cell != secondLineNum && cell != thirdLineNum) { foreach (var line in lines) { if (line.GetCell(cell).EliminateCandidate(candidate, Name)) { lineProgress.Add(line); } } } } foreach (var line in lineProgress) { _logger.Log(Name, $"Removed {candidate}s in {line.Description}"); progress = true; } } } } } } } return(progress); }
public void Info(string message, params object[] arguments) { infoLogger.Log(message, arguments); }
public bool Apply(Puzzle puzzle) { bool progress = false; // For every 2 candidate cell, look for other cells it can see that also have 2 candidates for (int r = 1; r <= 9; r++) { var row = puzzle.GetRow(r); for (int c = 1; c <= 9; c++) { var hingeCell = row.GetCell(c); if (!hingeCell.Filled && hingeCell.Candidates.Count == 2) { // Search the row, col and box for other cells with 2 candidates where 1 candidate is the same var col = puzzle.GetColumn(c); var box = puzzle.GetBox(Helpers.BoxNumber(c, r)); var potentialCells = new HashSet <Cell>(); for (int i = 1; i <= 9; i++) { if (i != c) { var rowCell = row.GetCell(i); if (rowCell.TwoCandidatesWith1Intersection(hingeCell)) { potentialCells.Add(rowCell); } } if (i != r) { var colCell = col.GetCell(i); if (colCell.TwoCandidatesWith1Intersection(hingeCell)) { potentialCells.Add(colCell); } } var boxCell = box.GetCell(i); if (boxCell.Col != c && boxCell.Row != r) { if (boxCell.TwoCandidatesWith1Intersection(hingeCell)) { potentialCells.Add(boxCell); } } // Find any pairs of cells that make sense???? var potentials = potentialCells.ToArray(); for (int p1 = 0; p1 < potentialCells.Count - 1; p1++) { for (int p2 = p1 + 1; p2 < potentialCells.Count; p2++) { var potential1 = potentials[p1]; var potential2 = potentials[p2]; var intersectionCandidates = potential1.Candidates.Intersect(potential2.Candidates); if (intersectionCandidates.Count() != 1) { // Y Wing cells need 1 candidate in common break; } var intersectionCandidate = intersectionCandidates.First(); if (hingeCell.Candidates.Contains(intersectionCandidate)) { // Candidate in common needs to be something not in the hinge cell break; } // We have found a Y Wing - find any candidates to delete // Get all the cells seen by p1 and p2 and remove candidate from the intersecting cells HashSet <Cell> seen1 = new HashSet <Cell>(puzzle.GetRow(potential1.Row).Cells); seen1.UnionWith(puzzle.GetColumn(potential1.Col).Cells); seen1.UnionWith(puzzle.GetBox(Helpers.BoxNumber(potential1.Col, potential1.Row)).Cells); HashSet <Cell> seen2 = new HashSet <Cell>(puzzle.GetRow(potential2.Row).Cells); seen2.UnionWith(puzzle.GetColumn(potential2.Col).Cells); seen2.UnionWith(puzzle.GetBox(Helpers.BoxNumber(potential2.Col, potential2.Row)).Cells); seen1.IntersectWith(seen2); seen1.Remove(potential1); seen1.Remove(potential2); var latestProgress = false; foreach (var elimCell in seen1) { if (elimCell.EliminateCandidate(intersectionCandidate, Name)) { latestProgress = true; } } if (latestProgress) { progress = true; _logger.Log(Name, $"({hingeCell.Col},{hingeCell.Row}), ({potential1.Col},{potential1.Row}), ({potential2.Col},{potential2.Row}) eliminated {intersectionCandidate}s successfully"); } } } } } } } return(progress); }
public void Warning(string message, params object[] arguments) { warningLogger.Log(message, arguments); }
private bool CheckGroup(Group group) { bool progress = false; // Get all the numbers not already filled in the group var candidates = new HashSet <char>(Constants.AllValues); for (int cell = 1; cell <= 9; cell++) { var cellData = group.GetCell(cell); if (cellData.Given || cellData.Filled) { candidates.Remove(cellData.Value); } } // Iterate all pairs of possible candidates var candidatesList = new List <char>(candidates); int combos = Helpers.Factorial(candidates.Count) / (2 * Helpers.Factorial(candidates.Count - 2)); // _logger.Log(Name, $"Hidden pairs: searching in {group.Description} with {combos} pair combos"); for (var i = 0; i < candidatesList.Count - 1; i++) { var candidate1 = candidatesList[i]; for (var j = i + 1; j < candidatesList.Count; j++) { var candidate2 = candidatesList[j]; // Find all cells containing both of the candidates var rejected = false; var inCells = new List <int>(); for (int cell = 1; cell <= 9; cell++) { var cellData = group.GetCell(cell); if (cellData.Given || cellData.Filled) { continue; } if (cellData.Candidates.Contains(candidate1)) { if (cellData.Candidates.Contains(candidate2)) { if (inCells.Count > 1) { // This pair is in too many cells rejected = true; break; } inCells.Add(cell); } else { rejected = true; break; } } else if (cellData.Candidates.Contains(candidate2)) { rejected = true; break; } } if (rejected) { continue; } if (inCells.Count == 2) { // We found a hidden pair! Expose it foreach (var cellNum in inCells) { var cellData = group.GetCell(cellNum); if (cellData.SetOnlyCandidates(new char[] { candidate1, candidate2 }, Name)) { _logger.Log(Name, $"Hidden pairs: hidden pair {candidate1}{candidate2} exposed in {group.Description}"); // Re-check all candidates before moving on to the next group //puzzle.CheckAllGroups(); progress = true; } } } } } return(progress); }