Пример #1
0
        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());
        }
Пример #2
0
        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);
                }
            }
        }
Пример #3
0
        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);
        }
Пример #4
0
        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);
                    }
                }
            }
        }
Пример #5
0
 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();
     }
 }
Пример #6
0
        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);
        }
Пример #7
0
        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;
        }
Пример #8
0
        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);
        }
Пример #9
0
        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);
        }
Пример #10
0
        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);
        }
Пример #11
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);
        }
Пример #12
0
        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);
        }
Пример #13
0
 public Puzzle(Puzzle puzzleToClone)
     : this()
 {
     CopyCellsFrom(puzzleToClone);
 }
Пример #14
0
        // 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);
                        }
                    }
                }
            }
        }
Пример #15
0
 protected override void ApplyReductions(Puzzle puzzle)
 {
     base.ApplyReductions(puzzle);
     ReduceUsingRowsOfGroups(puzzle);
     ReduceUsingsColumnsOfGroups(puzzle);
 }
Пример #16
0
        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;
        }
Пример #17
0
        // 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);
                        }
                    }
                }
            }
        }