public Solver(Sudoku sudoku, Func <Sudoku, T> resultSelector, Random rnd = null) { _sudoku = sudoku; _resultSelector = resultSelector; _rnd = rnd; }
/// <summary> /// Generates a new puzzle based specified <paramref name="solved"/>. /// </summary> /// <param name="solved">The solution the puzzle will be generated for.</param> /// <param name="rnd">The random.</param> /// <param name="backtracking">indicates how backtracking is used to reduce the number of predefined values.</param> public Sudoku(Sudoku solved, Random rnd, SudokuBacktrackingOption backtracking = SudokuBacktrackingOption.Allowed) : this(solved.Cross, solved.Hyper) { if (solved == null) { throw new ArgumentNullException(nameof(solved)); } if (rnd == null) { throw new ArgumentNullException(nameof(rnd)); } if (solved.GetState() != SudokuState.Solved) { throw new ArgumentException("specified sudoku has to be solved"); } BcfTransaction mainTransaction = null; // a loop to enforce "Not Solvable With AutoComplete" when backtracking is SudokuBacktrackingOption.Required do { if (mainTransaction != null) { mainTransaction.Rollback(); } mainTransaction = _model.BeginTransaction(); // indexes of filled cells var predefined = new List <int>(); // generate a puzzle solvable using autocomplete { var subtransaction = _model.BeginTransaction(); var cellEntries = _model.CellTable.Select((cellRow, index) => new { cellRow, index }); while (GetState() != SudokuState.Solved) { cellEntries = cellEntries.Where(entry => entry.cellRow.Value == 0).Randomize(rnd); var indexOfBest = -1; var bestCellCandidateCount = 0; foreach (var cellEntry in cellEntries) { var cellCandidateCount = Util.BitCountRegister[cellEntry.cellRow.AllowedValues]; if (cellCandidateCount > bestCellCandidateCount) { bestCellCandidateCount = cellCandidateCount; indexOfBest = cellEntry.index; if (bestCellCandidateCount == 9) { break; // 9 is best possible result } } } _model.CellTable[indexOfBest].Value = solved._model.CellTable[indexOfBest].Value; AutoComplete(); predefined.Add(indexOfBest); } // AutoComplete() has updated the model. In next step we need values of predefined only // so rollback subtransaction subtransaction.Rollback(); } { // and set predefined only foreach (var rowIndex in predefined) { _model.CellTable[rowIndex].Value = solved._model.CellTable[rowIndex].Value; } } // try to omit each predefined value and if the puzzle remains solvable - remove it. { foreach (var rowIndex in predefined) { var subtransaction = _model.BeginTransaction(); _model.CellTable[rowIndex].Value = 0; if (backtracking != SudokuBacktrackingOption.Disabled) { if (Solve(x => 0).Take(2).Count() == 1) { subtransaction.Commit(); } } else { if (CanBeSolvedUsingAutoComplete()) { subtransaction.Commit(); } } if (!subtransaction.IsCommitted) { subtransaction.Rollback(); } } } } while (backtracking == SudokuBacktrackingOption.Required && CanBeSolvedUsingAutoComplete()); }