private static void SolveSudokuProblem() { var initialState = new SudokuState( new int[9, 9] { { 0, 0, 5, 0, 3, 0, 0, 6, 2 }, { 0, 3, 0, 8, 9, 0, 0, 0, 1 }, { 0, 2, 9, 4, 0, 0, 0, 0, 0 }, { 0, 0, 0, 2, 6, 0, 0, 0, 0 }, { 8, 9, 3, 0, 0, 0, 0, 0, 0 }, { 6, 0, 0, 0, 0, 0, 8, 5, 0 }, { 3, 6, 8, 9, 0, 0, 0, 0, 0 }, { 0, 0, 4, 0, 0, 0, 0, 9, 6 }, { 0, 0, 0, 0, 4, 3, 0, 0, 0 } }); var actionFunction = new SudokuActionFunction(); var resultFunction = new SudokuResultFunction(); var goalTest = new SudokuGoalTest(); var stepCost = new SudokuStepCost(); var searchAlgorithm = new GraphSearch <SudokuState, SudokuAction>(); SolveProblem(actionFunction, resultFunction, goalTest, stepCost, initialState, searchAlgorithm); }
private void Solve(SudokuPuzzle puzzle, string input, string expected) { var state = SudokuState.Parse(input); var solver = new SudokuSolver(puzzle); var sw = Stopwatch.StartNew(); var actual = solver.Solve(state); sw.Stop(); Console.WriteLine("Elapsed: {0:#,##0.#####} ms", sw.Elapsed.TotalMilliseconds); Console.WriteLine(actual); Assert.IsTrue(actual.IsSolved, "The puzzle is not solved."); Assert.AreEqual(SudokuState.Parse(expected), actual); }
public void Parse_3x3_ReturnSudokuState3x3() { var puzzle = SudokuState.Parse(@" 53.|.7.|... 6..|195|... .98|...|.6. ---+---+--- 8..|.6.|..3 4..|8.3|..1 7..|.2.|..6 ---+---|--- .6.|...|28. ...|419|..5 ...|.8.|.79" ); Assert.AreEqual(3, puzzle.Size); }
/// <summary>Reduces hidden singles.</summary> /// <remarks> /// Very frequently, there is only one candidate for a given row, column or /// sub square, but it is hidden among other candidates. /// </remarks> private ReduceResult ReduceHiddenSingles(ReduceResult result, SudokuState state) { if (SkipMethod(SudokuSolverMethods.HiddenSingles, result)) { return result; } foreach (var region in Puzzle.Regions) { foreach (var singleValue in Puzzle.SingleValues) { var cnt = 0; var found = -1; foreach (var index in region) { var val = state[index]; if ((val & singleValue) != SudokuPuzzle.Invalid) { unchecked { cnt++; } if (state.IsKnown(index)) { found = -1; break; } else if (cnt == 1) { found = index; } } } if (cnt == 1 && found != -1) { result |= state.AndMask(found, singleValue); } else if (cnt == 0) { return ReduceResult.Inconsistend; } } } return result; }
/// <summary>Solves a Sudoku puzzle given the Sudoku state.</summary> public SudokuState Solve(SudokuState sudokuState) { // As states are not immutable, create a copy. var state = sudokuState.Copy(); var result = ReduceResult.Reduced; while (result == ReduceResult.Reduced) { result = ReduceResult.None; result |= ReduceSingles(result, state); result |= ReduceHiddenSingles(result, state); result |= ReduceLockedCandidates(result, state); result |= ReduceNakedPairs(result, state); result |= ReduceNakedTriples(result, state); result |= ReduceNakedQuads(result, state); } if (result.HasFlag(ReduceResult.Inconsistend)) { throw new InvalidPuzzleException(); } return state; }
/// <summary>Reduces singles.</summary> /// <remarks> /// Any cells which have only one candidate can safely be assigned that value. /// /// It is very important whenever a value is assigned to a cell, that this /// value is also excluded as a candidate from all other blank cells sharing /// the same row, column and sub square. /// </remarks> private ReduceResult ReduceSingles(ReduceResult result, SudokuState state) { if (SkipMethod(SudokuSolverMethods.Singles, result)) { return result; } result = ReduceResult.Reduced; while (result == ReduceResult.Reduced) { result = ReduceResult.None; for (var index1 = 0; index1 <= Puzzle.MaximumIndex; index1++) { if (state.IsKnown(index1)) { foreach (var group in Puzzle.Lookup[index1]) { foreach (var index0 in group) { if (index0 != index1) { result |= state.Exclude(index0, index1); } } } } } } return result; }
/// <summary>Reduces naked triples.</summary> private ReduceResult ReduceNakedTriples(ReduceResult result, SudokuState state) { if (SkipMethod(SudokuSolverMethods.NakedTriples, result)) { return result; } foreach (var singleValue in Puzzle.SingleValues) { foreach (var region in Puzzle.Regions) { var index0 = -1; var index1 = -1; var index2 = -1; var match = singleValue; foreach (var index in region) { var value = state[index]; if (!state.IsKnown(index) && (value & match) != SudokuPuzzle.Invalid) { match |= value; /**/ if (index0 == -1) { index0 = index; } else if (index1 == -1) { index1 = index; } else if (index2 == -1) { index2 = index; } else { index2 = -1; break; } } } // We found 3 cells. if (index2 != -1 && SudokuCell.Count(match) == 3) { foreach (var index in region) { if (index != index0 && index != index1 && index != index2) { result |= state.AndMask(index, ~match); } } } } } return result; }
/// <summary>Reduces options that should be in the intersection.</summary> private ReduceResult ReduceLockedCandidates(ReduceResult result, SudokuState state) { if (SkipMethod(SudokuSolverMethods.LockedCandidates, result)) { return result; } foreach (var region in Puzzle.Regions) { foreach (var other in region.Intersected) { ulong combined = 0; foreach (var index in region) { if (!other.Contains(index)) { combined |= state[index]; } } // There are options that should be in the intersection. if (combined != Puzzle.Unknown) { foreach (var index in other) { if (!region.Contains(index)) { var val = state[index]; var nw = val & combined; result |= state.AndMask(index, nw); } } } } } return result; }
public SudokuSolver(SudokuState sudokuState, SudokuBlock sudokuBlock) { _sudokuState = sudokuState; _sudokuBlock = sudokuBlock; }