public int Solve(byte[] puzzle, byte[] solution, out int difficulty) { difficulty = 0; var context = new PuzzleSolverContext(puzzle.ToArray(), solution); var possibilities = InitializePossibilities(puzzle); if (!PuzzleIsValid(puzzle, possibilities)) { return(0); } RecursiveSolve(context, possibilities, 0); // Calculate a difficulty score var empty = 0; foreach (var value in puzzle) { if (!CellIsSet(value)) { empty++; } } var multiplier = 1; while (multiplier <= puzzle.Length) { multiplier *= 10; } difficulty = context.BranchDifficulty * multiplier + empty; return(context.Count); }
public void RecursiveSolve(PuzzleSolverContext context, BitArray[] possibilities, int difficulty) { var cellIndex = FindCellWithFewestPossibilities(context.Problem, possibilities); if (cellIndex < 0) { if (context.Count == 0) { context.BranchDifficulty = difficulty; if (context.Solution != null) { Array.Copy(context.Problem, context.Solution, context.Solution.Length); } } context.Count++; return; } var mask = new BitArray(possibilities[cellIndex]); int branchingFactor; // If we can't determine a cell value, see if set-oriented // backtracking provides a smaller branching factor. if (CountSetBits(mask) > 1) { var offsets = new int[Size]; var setSize = PossibilityAnalysis(context.Problem, possibilities, ref offsets, out var value); if (setSize >= 0 && setSize < CountSetBits(mask)) { branchingFactor = setSize - 1; difficulty += branchingFactor * branchingFactor; for (var i = 0; i < setSize; i++) { var offset = offsets[i]; var newPossibilities = possibilities .Select(x => new BitArray(x)) .ToArray(); EliminatePossibilities(newPossibilities, offset % Size, offset / Size, value); context.Problem[offset] = Convert.ToByte(value); RecursiveSolve(context, newPossibilities, difficulty); context.Problem[offset] = 0; if (context.Count >= 2) { return; } } return; } } // Otherwise, fall back to cell-oriented backtracking. branchingFactor = CountSetBits(mask) - 1; difficulty += branchingFactor * branchingFactor; for (var i = 0; i < Size; i++) { if (mask[i]) { var newPossibilities = possibilities .Select(x => new BitArray(x)) .ToArray(); EliminatePossibilities(newPossibilities, cellIndex % Size, cellIndex / Size, i + 1); context.Problem[cellIndex] = Convert.ToByte(i + 1); RecursiveSolve(context, newPossibilities, difficulty); if (context.Count >= 2) { return; } } } context.Problem[cellIndex] = 0; }