internal SudokuProgress CheckForOnlyOnePossibility() { // Check if there is only one number within this rule that can have a specific value IList <int> existingNumbers = _tiles.Select(tile => tile.Value).Distinct().ToList(); SudokuProgress result = SudokuProgress.NO_PROGRESS; foreach (int value in Enumerable.Range(1, _tiles.Count)) { if (existingNumbers.Contains(value)) // this rule already has the value, skip checking for it { continue; } var possibles = _tiles.Where(tile => !tile.HasValue && tile.IsPossibleValue(value)).ToList(); if (possibles.Count == 0) { return(SudokuProgress.FAILED); } if (possibles.Count == 1) { possibles.First().Fix(value, "Only possible in rule " + ToString()); result = SudokuProgress.PROGRESS; } } return(result); }
internal SudokuProgress Solve() { // If both are null, return null (indicating no change). If one is null, return the other. Else return result1 && result2 SudokuProgress result1 = RemovePossibles(); SudokuProgress result2 = CheckForOnlyOnePossibility(); return(SudokuTile.CombineSolvedState(result1, result2)); }
public IEnumerable <SudokuBoard> Solve() { ResetSolutions(); SudokuProgress simplify = SudokuProgress.PROGRESS; while (simplify == SudokuProgress.PROGRESS) { simplify = Simplify(); } if (simplify == SudokuProgress.FAILED) { yield break; } // Find one of the values with the least number of alternatives, but that still has at least 2 alternatives var query = from rule in rules from tile in rule where tile.PossibleCount > 1 orderby tile.PossibleCount ascending select tile; SudokuTile chosen = query.FirstOrDefault(); if (chosen == null) { // The board has been completed, we're done! yield return(this); yield break; } Console.WriteLine("SudokuTile: " + chosen.ToString()); foreach (var value in Enumerable.Range(1, _maxValue)) { // Iterate through all the valid possibles on the chosen square and pick a number for it if (!chosen.IsValuePossible(value)) { continue; } var copy = new SudokuBoard(this); copy.Tile(chosen.X, chosen.Y).Fix(value, "Trial and error"); foreach (var innerSolution in copy.Solve()) { yield return(innerSolution); } } yield break; }
internal static SudokuProgress CombineSolvedState(SudokuProgress a, SudokuProgress b) { switch (a) { case SudokuProgress.FAILED: return(a); case SudokuProgress.NO_PROGRESS: return(b); case SudokuProgress.PROGRESS: return(b == SudokuProgress.FAILED ? b : a); } throw new InvalidOperationException($"Invalid value for {nameof(a)}"); }
internal static SudokuProgress CombineSolvedState(SudokuProgress a, SudokuProgress b) { if (a == SudokuProgress.FAILED) { return(a); } if (a == SudokuProgress.NO_PROGRESS) { return(b); } if (a == SudokuProgress.PROGRESS) { return(b == SudokuProgress.FAILED ? b : a); } throw new InvalidOperationException("Invalid value for a"); }
internal SudokuProgress Simplify() { SudokuProgress result = SudokuProgress.NO_PROGRESS; bool valid = CheckValid(); if (!valid) { return(SudokuProgress.FAILED); } foreach (SudokuRule rule in rules) { result = SudokuTile.CombineSolvedState(result, rule.Solve()); } return(result); }
internal SudokuProgress RemovePossibles() { // Tiles that have numbers already IEnumerable <SudokuTile> withNumber = _tiles.Where(tile => tile.HasValue); // Tiles without a number IEnumerable <SudokuTile> withoutNumber = _tiles.Where(tile => !tile.HasValue); // The existing numbers in this rule IEnumerable <int> existingNumbers = new HashSet <int>(withNumber.Select(tile => tile.Value).Distinct().ToList()); SudokuProgress result = SudokuProgress.NO_PROGRESS; foreach (SudokuTile tile in withoutNumber) { result = SudokuTile.CombineSolvedState(result, tile.RemovePossibles(existingNumbers)); } return(result); }
internal SudokuProgress RemovePossibles(IEnumerable <int> existingNumbers) { if (_blocked) { return(SudokuProgress.NO_PROGRESS); } // Takes the current possible values and removes the ones existing in `existingNumbers` possibleValues = new HashSet <int>(possibleValues.Where(x => !existingNumbers.Contains(x))); SudokuProgress result = SudokuProgress.NO_PROGRESS; if (possibleValues.Count == 1) { Fix(possibleValues.First(), "Only one possibility"); result = SudokuProgress.PROGRESS; } if (possibleValues.Count == 0) { return(SudokuProgress.FAILED); } return(result); }
public IEnumerable <SudokuBoard> Solve() { SudokuProgress Simplify() { bool valid = _rules.All(rule => rule.CheckValid()); if (!valid) { return(SudokuProgress.FAILED); } return(_rules.Aggregate(SudokuProgress.NO_PROGRESS, (progress, rule) => SudokuTile.CombineSolvedState(progress, rule.Solve()))); } // reset solution foreach (SudokuTile tile in _tiles) { tile.ResetPossibles(); } SudokuProgress simplify = SudokuProgress.PROGRESS; while (simplify == SudokuProgress.PROGRESS) { simplify = Simplify(); } if (simplify == SudokuProgress.FAILED) { yield break; } // Find one of the values with the least number of alternatives, but that still has at least 2 alternatives IEnumerable <SudokuTile> query = from rule in _rules from tile in rule where tile.PossibleCount > 1 orderby tile.PossibleCount ascending select tile; SudokuTile chosen = query.FirstOrDefault(); if (chosen == null) { // The board has been completed, we're done! yield return(this); yield break; } foreach (int value in Enumerable.Range(1, _maxValue)) { // Iterate through all the valid possibles on the chosen square and pick a number for it if (!chosen.IsValuePossible(value)) { continue; } SudokuBoard copy = new SudokuBoard(this); copy[chosen.X, chosen.Y].Fix(value, "Trial and error"); foreach (SudokuBoard innerSolution in copy.Solve()) { yield return(innerSolution); } } yield break; }
internal static SudokuProgress CombineSolvedState(SudokuProgress a, SudokuProgress b) { if (a == SudokuProgress.FAILED) return a; if (a == SudokuProgress.NO_PROGRESS) return b; if (a == SudokuProgress.PROGRESS) return b == SudokuProgress.FAILED ? b : a; throw new InvalidOperationException("Invalid value for a"); }