public IEnumerator <T> GetEnumerator() { var state = _sudoku.GetState(); if (state == SudokuState.Error) { return(Enumerable.Empty <T>().GetEnumerator()); } if (state == SudokuState.Solved) { return(Enumerable.Repeat(_resultSelector(_sudoku), 1).GetEnumerator()); } return(new SolutionEnumerator <T>(_sudoku, _resultSelector, _rnd)); }
public bool MoveNext() { while (_valueEnumerator.MoveNext()) { // rollback previous enumeration step result if (!ReferenceEquals(null, _transaction)) { _transaction.Rollback(); } // create a subtransaction _transaction = _sudoku._model.BeginTransaction(); // assign value _cell.Value = _valueEnumerator.Current; // autocomplete other cells _sudoku.AutoComplete(); // in case of error try next value (if there is one) if (_sudoku.GetState() != SudokuState.Error) { return(true); } } return(false); }
/// <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()); }