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);
        }