Beispiel #1
0
 // WI: ? make virtual calls
 /// <summary>
 /// Converts data in the specified binary file to the instance of grid.
 /// </summary>
 /// <param name="binaryFile">Binary file containing 
 /// compressed grid.</param>
 /// <returns>Instance of a particular grid.</returns>
 public static SudokuGrid FromBinary(FileStream binaryFile)
 {
     SudokuGridConstraints constraints;
     SudokuGridMetrics metrics;
     byte[] numbersKinds;
     string content = SudokuConverter.ToText(binaryFile, out constraints, out numbersKinds, out metrics);
     //string content = SudokuConverter.ToText(binaryFile, out constraints, out metrics);
     switch (metrics.MaximumNumber)
     {
         case 9:
             var grid = new SudokuGrid9x3(constraints);
             grid.IterateLinesXY((x, y) =>
             {
                 var cell = new SudokuGridCell(grid, 
                     new SudokuGridPosition(x, y, false), 
                     byte.Parse(content[metrics.MaximumNumber * y + x].ToString()));
                 //cell.NumberChanged += (s, e) => grid.OnCellNumberChanged(new CellNumberChangedEventArgs((SudokuGridCell)s));
                 //grid.cells[y, x] = cell;
                 AssignNewCell(grid, cell);
             });
             return grid;
         default:
             throw new SudokuGridNotSupportedException();
     }
 }
        // TODO: rename
        private void RegisterCellAsClue(SudokuGridCell cell)
        {
            rowsOfNumbers[cell.Position.Y].Add(cell.Number);
            columnsOfNumbers[cell.Position.X].Add(cell.Number);
            byte x, y;

            FindContainingBlock(cell, out x, out y);
            blocksOfNumbers[y, x].Add(cell.Number);
            --emptyCellsCount;
        }
        private SudokuGridCell FindFirstCellWithMinimumCandidates(SudokuGrid grid)
        {
            byte           minCandidates     = grid.Metrics.MaximumNumber;
            SudokuGridCell minCandidatesCell = null;

            grid.IterateLinesXY((x, y) =>
            {
                var currCell = grid.Cells[y, x];
                if (currCell.Candidates != null &&
                    currCell.Candidates.Count < minCandidates)
                {
                    minCandidates     = (byte)currCell.Candidates.Count;
                    minCandidatesCell = currCell;
                }
            });
            return(minCandidatesCell);
        }
        // TODO: implement the remaining functionality to find all possible solutions
        // TODO: translate in English
        // Алгоритм: последовательный проход по клеткам слева направо и вниз
        // и усечение мн-ва вариантов цифр,
        // которые могут находиться в данной клетке; единственный
        // оставшийся вариант подставляется в клетку. Каждый раз,
        // как совершается повторный обход доски, должна заполняться
        // хотя бы одна клетка. Иначе алгоритм зацикливается,
        // и запускается другой алгоритм:
        // в первую найденную клетку, имеющую наименьшее кол-во вариантов,
        // подставляется наименьший из них (несущественно) и поиск продолжается
        // на специальной доске assumptionsGrid по исходному
        // алгоритму: если полученное решение непротиворечиво, то
        // оно становится окончательным. Иначе: а) если получаем
        // противоречие, то исходная подстановка неверна и,
        // вернувшись к точке осущствления подстановки,
        // подставляем другую цифру, вновь применяя исохдный алгоритм;
        // б) если получаем зацикливание, то снова находим эл-т
        // с наименьшим кол-вом вариантов, подставляем один из них
        // и применяем исходный алгоритм.
        // TODO: protected override List<SudokuGridCell[,]> SearchSolutions()
        protected override bool SearchSolutionDebug()
        {
            forbiddenCandidates.Clear();
            Grid.IterateLinesXY((x, y) =>
            {
                SudokuGridCell cell = Grid.Cells[y, x];
                if (cell.IsClue)
                {
                    RegisterCellAsClue(cell);
                }
            });

            var resultCells = _SearchSolutionDebug();

            if (resultCells == null)
            {
                return(false);
            }
            Grid.Cells = resultCells;
            Reset();
            return(true);
        }
        private SudokuGridCell[,] MakeAssumptionInCell(ref SudokuGridPosition? minCandidatesCellPos)
        {
            if (!minCandidatesCellPos.HasValue)
            {
                // TODO: optimize: lookup that kind of cell repetitively when its number of candidates changes (decreases): compare against the minimum value from a variable (where to put it?)
                minCandidatesCellPos = FindFirstCellWithMinimumCandidates(Grid).Position;
            }
            var minCandidatesCell = Grid.Cells[minCandidatesCellPos.Value.Y, minCandidatesCellPos.Value.X];

            byte assumedNumber = minCandidatesCell.Candidates.ToArray()[0];

            assumptionsGrid = (SudokuGrid)Grid.GetType()
                              .GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,
                                              null, CallingConventions.HasThis, new[] { typeof(SudokuGridConstraints) }, null)
                              .Invoke(new [] { (object)Grid.Constraints });

            // BUG: !!! as we use the standard grid here, which was not initially intended to be used anywhere except for decode-and-construct scenarios, by the time we reach this code the assumptionsGrid's cells are not initialized yet -- consider introducing some sort of temporary 'grid' (most probably, a kind of 'cells-only layer'). For that reason one of the ctor's code sections was moved into the Reset() method
            var assumptionsGridSolver = new SudokuSolvingIterationAssumptionTechnique(assumptionsGrid);

            // WI: cell-to-cell iteration is a very intensive process -- use cells array cloning instead with targeted minCandidatesCell... altering
            var minCandidatesCellPosCopy = minCandidatesCellPos.Value;

            assumptionsGrid.IterateLinesXY((x, y) =>
            {
                SudokuGridCell currCell;
                var currPos = new SudokuGridPosition(x, y, false);
                if (currPos.Equals(minCandidatesCellPosCopy))
                {
                    currCell = new SudokuGridCell(assumptionsGrid, currPos, assumedNumber);
                    assumptionsGridSolver.lastFilledPos   = currPos;
                    assumptionsGridSolver.currCheckingPos = currPos;
                    assumptionsGridSolver.currCheckingPos.Shift(1, assumptionsGrid.Metrics);
                }
                else
                {
                    currCell = new SudokuGridCell(assumptionsGrid, currPos, Grid.Cells[y, x].Number);
                    if (currCell.Candidates != null)
                    {
                        // just to bring into accord with the latest value from the 'parent' grid
                        currCell.Candidates.IntersectWith(Grid.Cells[y, x].Candidates);
                    }
                }
                SudokuGrid.AssignNewCell(assumptionsGrid, currCell);
                //assumptionsGrid.Cells[y, x] = currCell;
            });

            //List<SudokuGridCell[,]> assumptionGridSolutuons = assumptionGridSolver.SearchSolutions();
            //if (assumptionGridSolutuons.Count > 0) // correct assumption was made
            //{
            //    return assumptionsGrid.Cells;
            //}

            //SudokuGridCell[,] solution;
            if (assumptionsGridSolver.SearchSolutionDebug()) // correct assumption was made
            {
                //return solution;
                return(assumptionsGrid.Cells);
            }

            // incorrect assumption was made (executes on contradiction only)
            minCandidatesCell.Candidates.Remove(assumedNumber);
            // if there were only two candidates, with assumedNumber being one of them, then the remaining one is a clue
            if (minCandidatesCell.IsClue)
            {
                minCandidatesCellPos = null;
                var newPos = new SudokuGridPosition(minCandidatesCell.Position.X, minCandidatesCell.Position.Y, false);
                lastFilledPos   = newPos;
                currCheckingPos = newPos;
                currCheckingPos.Shift(1, Grid.Metrics);
            }
            return(null);
        }
 // WI: consider moving to cell class
 private void FindContainingBlock(SudokuGridCell cell, out byte x, out byte y)
 {
     x = (byte)(cell.Position.X / cell.ParentGrid.Metrics.BlockWidth);
     y = (byte)(cell.Position.Y / cell.ParentGrid.Metrics.BlockHeight);
 }
Beispiel #7
0
 // BUG: used to avoid buggy duplicate-code decision in solver classes -- get rid of as soon as possible
 internal static void AssignNewCell(SudokuGrid grid, SudokuGridCell cell)
 {
     cell.NumberChanged += (s, e) => grid.OnCellNumberChanged(new CellNumberChangedEventArgs((SudokuGridCell)s));
     grid.cells[cell.Position.Y, cell.Position.X] = cell;
 }
Beispiel #8
0
 public CellNumberChangedEventArgs(SudokuGridCell cell) { this.cell = cell; }
 // TODO: rename
 private void RegisterCellAsClue(SudokuGridCell cell)
 {
     rowsOfNumbers[cell.Position.Y].Add(cell.Number);
     columnsOfNumbers[cell.Position.X].Add(cell.Number);
     byte x, y;
     FindContainingBlock(cell, out x, out y);
     blocksOfNumbers[y, x].Add(cell.Number);
     --emptyCellsCount;
 }
        private SudokuGridCell[,] MakeAssumptionInCell(ref SudokuGridPosition? minCandidatesCellPos)
        {
            if (!minCandidatesCellPos.HasValue)
            {
                // TODO: optimize: lookup that kind of cell repetitively when its number of candidates changes (decreases): compare against the minimum value from a variable (where to put it?)
                minCandidatesCellPos = FindFirstCellWithMinimumCandidates(Grid).Position;
            }
            var minCandidatesCell = Grid.Cells[minCandidatesCellPos.Value.Y, minCandidatesCellPos.Value.X];

            byte assumedNumber = minCandidatesCell.Candidates.ToArray()[0];

            assumptionsGrid = (SudokuGrid)Grid.GetType()
                .GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,
                null, CallingConventions.HasThis, new[] { typeof(SudokuGridConstraints) }, null)
                .Invoke(new [] { (object)Grid.Constraints });

            // BUG: !!! as we use the standard grid here, which was not initially intended to be used anywhere except for decode-and-construct scenarios, by the time we reach this code the assumptionsGrid's cells are not initialized yet -- consider introducing some sort of temporary 'grid' (most probably, a kind of 'cells-only layer'). For that reason one of the ctor's code sections was moved into the Reset() method
            var assumptionsGridSolver = new SudokuSolvingIterationAssumptionTechnique(assumptionsGrid);

            // WI: cell-to-cell iteration is a very intensive process -- use cells array cloning instead with targeted minCandidatesCell... altering
            var minCandidatesCellPosCopy = minCandidatesCellPos.Value;
            assumptionsGrid.IterateLinesXY((x, y) =>
            {
                SudokuGridCell currCell;
                var currPos = new SudokuGridPosition(x, y, false);
                if (currPos.Equals(minCandidatesCellPosCopy))
                {
                    currCell = new SudokuGridCell(assumptionsGrid, currPos, assumedNumber);
                    assumptionsGridSolver.lastFilledPos = currPos;
                    assumptionsGridSolver.currCheckingPos = currPos;
                    assumptionsGridSolver.currCheckingPos.Shift(1, assumptionsGrid.Metrics);
                }
                else
                {
                    currCell = new SudokuGridCell(assumptionsGrid, currPos, Grid.Cells[y, x].Number);
                    if (currCell.Candidates != null)
                    {
                        // just to bring into accord with the latest value from the 'parent' grid
                        currCell.Candidates.IntersectWith(Grid.Cells[y, x].Candidates);
                    }
                }
                SudokuGrid.AssignNewCell(assumptionsGrid, currCell);
                //assumptionsGrid.Cells[y, x] = currCell;
            });

            //List<SudokuGridCell[,]> assumptionGridSolutuons = assumptionGridSolver.SearchSolutions();
            //if (assumptionGridSolutuons.Count > 0) // correct assumption was made
            //{
            //    return assumptionsGrid.Cells;
            //}

            //SudokuGridCell[,] solution;
            if (assumptionsGridSolver.SearchSolutionDebug()) // correct assumption was made
            {
                //return solution;
                return assumptionsGrid.Cells;
            }

            // incorrect assumption was made (executes on contradiction only)
            minCandidatesCell.Candidates.Remove(assumedNumber);
            // if there were only two candidates, with assumedNumber being one of them, then the remaining one is a clue
            if (minCandidatesCell.IsClue)
            {
                minCandidatesCellPos = null;
                var newPos = new SudokuGridPosition(minCandidatesCell.Position.X, minCandidatesCell.Position.Y, false);
                lastFilledPos = newPos;
                currCheckingPos = newPos;
                currCheckingPos.Shift(1, Grid.Metrics);
            }
            return null;
        }
 // WI: consider moving to cell class
 private void FindContainingBlock(SudokuGridCell cell, out byte x, out byte y)
 {
     x = (byte)(cell.Position.X / cell.ParentGrid.Metrics.BlockWidth);
     y = (byte)(cell.Position.Y / cell.ParentGrid.Metrics.BlockHeight);
 }