Beispiel #1
0
        public static SudokuBoard SolveInternal(SudokuBoard sudokuBoard)
        {
            if (!sudokuBoard.IsValid())
            {
                return(null);
            }

            var cells = Enumerable.Range(0, NumberOfBoardCellsInSingleDirection)
                        .SelectMany(
                vni =>
                Enumerable.Range(0, NumberOfBoardCellsInSingleDirection)
                .Select(hni => new Cell(hni, vni, sudokuBoard._numbers))).ToList();

            Debug.Assert(cells.Count == NumberOfBoardCellsInSingleDirection * NumberOfBoardCellsInSingleDirection);

            var rows = Enumerable.Range(0, NumberOfBoardCellsInSingleDirection).Select(rowIndex =>
            {
                var rowCells = cells.Where(c => c.VerticalIndex == rowIndex).ToList();
                return(new SolutionRegion(rowCells));
            }).ToList();

            // TODO: rows and cols are mismatched, imporve just for readability
            var columns = Enumerable.Range(0, NumberOfBoardCellsInSingleDirection).Select(columnIndex =>
            {
                var columnCells = cells.Where(c => c.HorizontalIndex == columnIndex).ToList();
                return(new SolutionRegion(columnCells));
            }).ToList();

            var quadrants = Enumerable.Range(0, NumberOfQuadrantsInOneDirection).SelectMany(quadrantHorizontalIndex =>
                                                                                            Enumerable.Range(0, NumberOfQuadrantsInOneDirection)
                                                                                            .Select(
                                                                                                quadrantVerticalIndex =>
                                                                                                new
            {
                QuadrantHorizontalIndex = quadrantHorizontalIndex,
                QuadrantVerticalIndex   = quadrantVerticalIndex
            })).Select(i =>
            {
                var quardantCells =
                    cells.Where(
                        c =>
                        c.HorizontalIndex >=
                        i.QuadrantHorizontalIndex * NumberOfQuadrantCellsInOneDirection &&
                        c.HorizontalIndex <
                        (i.QuadrantHorizontalIndex + 1) * NumberOfQuadrantCellsInOneDirection &&
                        c.VerticalIndex >=
                        i.QuadrantVerticalIndex * NumberOfQuadrantCellsInOneDirection &&
                        c.VerticalIndex <
                        (i.QuadrantVerticalIndex + 1) * NumberOfQuadrantCellsInOneDirection
                        ).ToList();

                return(new SolutionRegion(quardantCells));
            }).ToList();

            Debug.Assert(new[] { rows, columns, quadrants }.All(s => s.Count == NumberOfBoardCellsInSingleDirection));

            var solutionRegions = rows.Concat(columns).Concat(quadrants).ToList();

            var cellsToSolutionRegionsMap = cells.ToDictionary(c => c,
                                                               c => (IReadOnlyCollection <SolutionRegion>)solutionRegions.Where(r => r.Cells.Contains(c)).ToList());

            Debug.Assert(cellsToSolutionRegionsMap.Values.All(s => s.Count == 3));

            var cellsToProcess = cells.Where(c => !c.Number.HasValue).ToList();
            var processedCellsBacktrackingInfos = new Stack <CellBacktractingInfo>();

            while (true)
            {
                var currentCellToProcess =
                    cellsToProcess.OrderBy(c => GetCellPossibleValues(c, cellsToSolutionRegionsMap[c]).Count)
                    .FirstOrDefault();

                if (currentCellToProcess == null)
                {
                    return(sudokuBoard);
                }

                Debug.Assert(!currentCellToProcess.Number.HasValue);

                var currentCellToProcessPossibleValues = GetCellPossibleValues(currentCellToProcess,
                                                                               cellsToSolutionRegionsMap[currentCellToProcess]);

                var currentCellToProcessBacktrackingInfo = new CellBacktractingInfo(currentCellToProcess,
                                                                                    currentCellToProcessPossibleValues);

                do
                {
                    var currentCellToProcessPossibleNumber =
                        currentCellToProcessBacktrackingInfo.GetNextPossibleNumber();

                    if (!currentCellToProcessPossibleNumber.HasValue)
                    {
                        currentCellToProcessBacktrackingInfo.Cell.Number = null;

                        if (!processedCellsBacktrackingInfos.Any())
                        {
                            return(null);
                        }

                        currentCellToProcessBacktrackingInfo = processedCellsBacktrackingInfos.Pop();
                        cellsToProcess.Add(currentCellToProcessBacktrackingInfo.Cell);
                        continue;
                    }

                    currentCellToProcessBacktrackingInfo.Cell.Number = currentCellToProcessPossibleNumber;
                    processedCellsBacktrackingInfos.Push(currentCellToProcessBacktrackingInfo);
                    cellsToProcess.Remove(currentCellToProcessBacktrackingInfo.Cell);
                    break;
                } while (true);
            }
        }