public void Solve(string url) { _browser = new IE(url); _puzzle = ParsePuzzle(); var solver = new Solver(); //solver.Reduced += SolverOnReduced; solver.Solve(_puzzle); SolverOnReduced(null, null); if (_puzzle.IsSolved) { return; } if (File.Exists("e:\\out.html")) { File.Delete("e:\\out.html"); } File.WriteAllText("e:\\out.html", GetPuzzleHtml()); }
public override void Solve(Puzzle puzzle) { base.Solve(puzzle); if (puzzle.HasErrors || puzzle.IsSolved) return; WriteLine("Not solved without guessing"); // Need to start guessing var cellToGuess= puzzle.Cells.OrderBy(c => c.PossibleAnswers.Count) .First(c => c.PossibleAnswers.Count > 1); WriteLine(string.Format("Guessing on {0} with [{1}]", cellToGuess.Coordinates, string.Join(", ", cellToGuess.PossibleAnswers))); var guessPuzzles = cellToGuess.PossibleAnswers .Select(p => CloneAndGuess(puzzle, cellToGuess, p)); foreach (var guessPuzzle in guessPuzzles.AsParallel()) { Solve(guessPuzzle); if (guessPuzzle.IsSolved && !guessPuzzle.HasErrors) { puzzle.CopyCellsFrom(guessPuzzle); } } }
private static void ApplyReductions(Puzzle puzzle) { var answeredCells = puzzle.Cells.Where(c => c.PossibleAnswers.Count == 1).ToArray(); foreach (var cell in answeredCells) { var answer = cell.PossibleAnswers[0]; var otherCells = cell.OtherColumnCells.Union(cell.OtherGroupCells).Union(cell.OtherRowCells); foreach (var otherCell in otherCells) { otherCell.RemoveAnwer(answer); } } var cellSets = puzzle.Rows.Union(puzzle.Columns).Union(puzzle.Groups); foreach (var cellSet in cellSets) { SetOnlyPossibleCell(cellSet); ReduceUsingMatchingCells(cellSet); } //ReduceUsingPerGroupColumnsOrRows(puzzle); ReduceUsingsGroupColumns(puzzle); //ReduceUsingsGroupRows(puzzle); }
private static void ReduceUsingsColumnsOfGroups(Puzzle puzzle) { var groupColumns = puzzle.Groups.GroupBy(g => g.First().Column).ToArray(); foreach (var groupRow in groupColumns) { var reducingGroups = groupRow.SelectMany(cells => cells) .GroupBy(c => c.PossibleAnswerHash) .Select(g => new { Cells = g.ToArray(), Answers = g.First().PossibleAnswers }) .Where(g => g.Cells.Length > 1) // There is an set .Where(g => g.Answers.Count == g.Cells.Length) // There is an excliving set .Where(g => g.Cells.GroupBy(c => c.Group).Count() == 2); // The set spans two groups // The remaining group cannot have any of the set's answers in any row used by the set foreach (var reducingGroup in reducingGroups) { var activeGroups = reducingGroup.Cells.Select(c => c.Group).Distinct().ToArray(); var affectedColumns = reducingGroup.Cells.Select(c => c.Column).Distinct().ToArray(); var affectedCells = groupRow.SelectMany(g => g) .Where(c => activeGroups.Contains(c.Group)) .Where(c => affectedColumns.Contains(c.Column)) .ToArray(); foreach (var affectedCell in affectedCells) { affectedCell.PossibleAnswers.RemoveAll(reducingGroup.Answers.Contains); } } } }
public void CopyCellsFrom(Puzzle puzzleToClone) { foreach (var cellSet in Cells.Zip(puzzleToClone.Cells, (n, o) => new {New = n, Old = o})) { cellSet.New.PossibleAnswers = cellSet.Old.PossibleAnswers.ToList(); } }
public void Solve(string url) { _browser = new IE(url); _puzzle = ParsePuzzle(); var solver = new GuessingSolver(); // solver.Reduced += SolverOnReduced; solver.Solve(_puzzle); SolverOnReduced(null, null); if (_puzzle.IsSolved) { return; } if(_puzzle.HasErrors) { System.Console.WriteLine("Puzzle has Errors"); } var tempPath = Path.GetTempPath(); string tempFileName; do { tempFileName = Path.Combine(tempPath, Guid.NewGuid().ToString().Remove(8) + ".html"); } while (File.Exists(tempFileName)); File.WriteAllText(tempFileName, GetPuzzleHtml()); Process.Start(tempFileName); }
private Puzzle CloneAndGuess(Puzzle puzzle, Cell cellToGuess, Number guess) { var clone = new Puzzle(puzzle); clone.Columns[cellToGuess.Column][cellToGuess.Row].SetAnswer(guess); RemoveAnsweredFromOtherPossibles(clone); return clone; }
public Cell(Puzzle puzzle, int row, int column) { _puzzle = puzzle; Row = row; Column = column; PossibleAnswers = new List<Number>(Validations.AllNumbers); Group = row - (row % 3) + (column / 3); }
protected virtual void ApplyReductions(Puzzle puzzle) { RemoveAnsweredFromOtherPossibles(puzzle); var cellSets = puzzle.Rows.Select(r => new { Name = "Row " + (r[0].Row + 1), Cells = r }) .Union(puzzle.Columns.Select(r => new { Name = "Column " + (r[0].Column + 1), Cells = r })) .Union(puzzle.Groups.Select(r => new { Name = "Group " + (r[0].Group + 1), Cells = r })); foreach (var cellSet in cellSets) { SetOnlyPossibleCell(cellSet.Cells, cellSet.Name); ReduceUsingMatchingCells(cellSet.Cells, cellSet.Name); } ReduceUsingPerGroupColumnsOrRows(puzzle); // ReduceUsingsGroupColumns(puzzle); // ReduceUsingsGroupRows(puzzle); }
public void Solve(Puzzle puzzle) { bool success; var count = puzzle.Cells.Count(c => c.PossibleAnswers.Count > 1); do { ApplyReductions(puzzle); var newCount = puzzle.Cells.Count(c => c.PossibleAnswers.Count > 1); success = count != newCount; count = newCount; OnReduced(new EventArgs()); } while (success && count > 0); }
protected void RemoveAnsweredFromOtherPossibles(Puzzle puzzle) { bool success; var count = puzzle.Cells.Count(c => c.PossibleAnswers.Count > 1); do { var answeredCells = puzzle.Cells.Where(c => c.PossibleAnswers.Count == 1).ToArray(); foreach (var cell in answeredCells) { if (!cell.PossibleAnswers.Any()) { return; } var answer = cell.PossibleAnswers[0]; var otherCells = cell.OtherColumnCells.Union(cell.OtherGroupCells).Union(cell.OtherRowCells) .Where(c => c.PossibleAnswers.Contains(answer)) .ToArray(); if (!otherCells.Any()) { continue; } foreach (var otherCell in otherCells) { WriteLine(string.Format("{0} looses {1} by answer on {2}", otherCell.Coordinates, (int)answer, cell.Coordinates)); otherCell.RemoveAnwer(answer); OnReduced(new EventArgs()); } } var newCount = puzzle.Cells.Count(c => c.PossibleAnswers.Count > 1); success = count != newCount; count = newCount; } while (success && count > 0 && !puzzle.HasErrors); }
public virtual void Solve(Puzzle puzzle) { bool success; var count = puzzle.Cells.Count(c => c.PossibleAnswers.Count > 1); do { System.Console.WriteLine("-- Starting Reductions --"); ApplyReductions(puzzle); var newCount = puzzle.Cells.Count(c => c.PossibleAnswers.Count > 1); success = count != newCount; count = newCount; } while (success && count > 0 && !puzzle.HasErrors); }
public Puzzle(Puzzle puzzleToClone) : this() { CopyCellsFrom(puzzleToClone); }
// If a number is only available in a singe row or column within a group, it must be used in that group. private static void ReduceUsingPerGroupColumnsOrRows(Puzzle puzzle) { foreach (var cellGroup in puzzle.Groups) { foreach (var number in Validations.AllNumbers) { var thisNumber = number; var matchingCells = cellGroup.Where(c => c.PossibleAnswers.Contains(thisNumber)).ToArray(); if (matchingCells.Length == 1) { continue; } var rowsUsed = matchingCells.Select(c => c.Row).ToArray(); var colsUsed = matchingCells.Select(c => c.Column).ToArray(); if (rowsUsed.Length == 1) { foreach (var cell in puzzle.Rows[rowsUsed[0]].Except(matchingCells)) { cell.RemoveAnwer(number); } } if (colsUsed.Length == 1) { foreach (var cell in puzzle.Columns[colsUsed[0]].Except(matchingCells)) { cell.RemoveAnwer(number); } } } } }
protected override void ApplyReductions(Puzzle puzzle) { base.ApplyReductions(puzzle); ReduceUsingRowsOfGroups(puzzle); ReduceUsingsColumnsOfGroups(puzzle); }
private Puzzle ParsePuzzle() { _inputs = new Dictionary<Tuple<int, int>, Element>(); _answers = new Dictionary<Tuple<int, int>, Number?>(); var frame = _browser.Frames[0]; var table = frame.Table(Find.ByClass("t")); var puzzle = new Puzzle(); for (var row = 0; row < 9; row++) { for (var column = 0; column < 9; column++) { var id = string.Format("f{0}{1}", column, row); var input = table.Element(Find.ById(id)); var number = GetNumber(input.GetAttributeValue("value")); var key = Tuple.Create(column, row); _inputs[key] = input; _answers[key] = number; if (number.HasValue) { puzzle.Rows[row][column].SetAnswer(number.Value); } } } return puzzle; }
// If a number is only available in a singe row or column within a group, it must be used in that group. // If a number must be in a certain group column, it cannot be in another group in the same column. private void ReduceUsingPerGroupColumnsOrRows(Puzzle puzzle) { foreach (var cellGroup in puzzle.Groups) { foreach (var number in Validations.AllNumbers) { var thisNumber = number; var matchingCells = cellGroup.Where(c => c.PossibleAnswers.Contains(thisNumber)).ToArray(); var matchingCellsString = string.Join(", ", matchingCells.Select(c => c.Coordinates)); if (matchingCells.Length == 1) { continue; } var rowsUsed = matchingCells.Select(c => c.Row).ToArray(); if (rowsUsed.Length == 1) { foreach (var cell in puzzle.Rows[rowsUsed[0]].Except(matchingCells)) { WriteLine(string.Format("{0} looses {1} by required cells {2} in group {3}", cell.Coordinates, (int)thisNumber, matchingCellsString, cellGroup[0].Group + 1)); cell.RemoveAnwer(number); } } var colsUsed = matchingCells.Select(c => c.Column).ToArray(); if (colsUsed.Length == 1) { foreach (var cell in puzzle.Columns[colsUsed[0]].Except(matchingCells)) { WriteLine(string.Format("{0} looses {1} by required cells {2} in group {3}", cell.Coordinates, (int)thisNumber, matchingCellsString, cellGroup[0].Group + 1)); cell.RemoveAnwer(number); } } } } }