private void SearchRec(DomainStore dstore, Sudoku sudoku) { // Select the sudoku cell with the smallest domain and try to fill it first Field f = dstore.GetFieldWithSmallestDomain(); if (f == null) { // There's no cell with a domain size > 1 left, the sudoku is filled completely dstore.UpdateSudoku(sudoku); return; } // Try to solve the sudoku with every possible value of the cell's domain HashSet <int> domain = dstore.GetDomain(f); foreach (int digit in domain) { DomainStore dstoreClone = new(dstore); dstoreClone.SetDomain(f, new HashSet <int>() { digit }); Search(dstoreClone, sudoku); if (sudoku.IsSolved()) { return; } else { // Prune this try and take a step back dstore.UpdateSudoku(sudoku); } } }
public DomainStore(DomainStore dstore) { InitFieldsByDomainSize(); for (int x = 0; x < Sudoku.Size; x++) { for (int y = 0; y < Sudoku.Size; y++) { SetDomain(x, y, dstore.GetDomain(x, y)); } } }
private bool Propagate(DomainStore dstore) { if (_cts.IsCancellationRequested) { return(false); } bool domainsHaveChanged = EliminateRowValues(dstore); domainsHaveChanged |= EliminateColumnValues(dstore); domainsHaveChanged |= EliminateRegionValues(dstore); return(domainsHaveChanged); }
private bool EliminateColumnValues(DomainStore dstore) { bool domainsHaveChanged = false; for (int y = 0; y < Sudoku.Size; y++) { List <Field> column = new(); for (int x = 0; x < Sudoku.Size; x++) { Field f = new(x, y); column.Add(f); } domainsHaveChanged |= Eliminate(dstore, column); } return(domainsHaveChanged); }
private void Search(DomainStore dstore, Sudoku sudoku) { if (_cts.IsCancellationRequested) { return; } // Try to solve sudoku via constraint propagation only while (Propagate(dstore)) { } dstore.UpdateSudoku(sudoku); if (sudoku.IsSolved()) { return; } // If that didn't work, perform a recursive search SearchRec(dstore, sudoku); }
private bool EliminateRegionValues(DomainStore dstore) { bool domainsHaveChanged = false; for (int i = 0; i < Sudoku.Size; i++) { List <Field> region = new(); for (int j = 0; j < Sudoku.Size; j++) { int x = (i / Sudoku.RegionSize) * Sudoku.RegionSize + j / Sudoku.RegionSize; int y = (i % Sudoku.RegionSize) * Sudoku.RegionSize + j % Sudoku.RegionSize; Field f = new(x, y); region.Add(f); } domainsHaveChanged |= Eliminate(dstore, region); } return(domainsHaveChanged); }
private bool Eliminate(DomainStore dstore, IEnumerable <Field> fields) { bool domainsHaveChanged = false; // Go through the cells and remeber the digits you see HashSet <int> seenDigits = new(); foreach (Field f in fields) { if (dstore.GetDomainSize(f) == 1) { HashSet <int> domain = dstore.GetDomain(f); foreach (int digit in domain) // Workaround to access first HashSet value quickly { seenDigits.Add(digit); break; } } } // Now, go through the cells again and update each cell's domain considering the digits you saw in the other cells foreach (Field f in fields) { // If a cells's domain has just a single digit left, it does not have to be changed anymore if (dstore.GetDomainSize(f) == 1) { continue; } HashSet <int> domain = dstore.GetDomain(f); HashSet <int> impossibleDigits = new(seenDigits); HashSet <int> subtraction = Util.Subtract(domain, impossibleDigits); // Prune this try if no digit can't be assigned to the cell if (subtraction.Count == 0) { return(false); } // If a cell's domain has to be updated based on the seen digits, do it and note that there was a change if (!domain.SetEquals(subtraction)) { domainsHaveChanged = true; dstore.SetDomain(f, subtraction); } } return(domainsHaveChanged); }