Esempio n. 1
0
        public override LogicResult StepLogic(SudokuSolver sudokuSolver, StringBuilder logicalStepDescription, bool isBruteForcing)
        {
            var board = sudokuSolver.Board;

            for (int i = 0; i < HEIGHT; i++)
            {
                for (int j = 0; j < WIDTH; j++)
                {
                    uint mask = board[i, j];
                    if (IsValueSet(mask))
                    {
                        continue;
                    }

                    int maskValueCount = ValueCount(mask);
                    if (maskValueCount == 2)
                    {
                        int val1 = MinValue(mask);
                        int val2 = MaxValue(mask);
                        if (val2 - val1 == 1)
                        {
                            // Two consecutive values in a cell means that all of its adjacent cells cannot be either of those values
                            bool haveChanges = false;
                            foreach (var otherPair in AdjacentCells(i, j))
                            {
                                uint        otherMask  = board[otherPair.Item1, otherPair.Item2];
                                LogicResult findResult = sudokuSolver.ClearMask(otherPair.Item1, otherPair.Item2, mask);
                                if (findResult == LogicResult.Invalid)
                                {
                                    logicalStepDescription.Clear();
                                    logicalStepDescription.Append($"{CellName(i, j)} with values {MaskToString(mask)} removes the only candidates {MaskToString(otherMask)} from {CellName(otherPair)}");
                                    return(LogicResult.Invalid);
                                }

                                if (findResult == LogicResult.Changed)
                                {
                                    if (!haveChanges)
                                    {
                                        logicalStepDescription.Append($"{CellName((i, j))} having candidates {MaskToString(mask)} removes those values from {CellName(otherPair)}");
                                        haveChanges = true;
                                    }
                                    else
                                    {
                                        logicalStepDescription.Append($", {CellName(otherPair)}");
                                    }
                                }
                            }
                            if (haveChanges)
                            {
                                return(LogicResult.Changed);
                            }
                        }
                        else if (val2 - val1 == 2)
                        {
                            // Values are 2 apart, which means adjacent cells can't be the value between those two
                            int  bannedVal   = val1 + 1;
                            uint clearMask   = 1u << (bannedVal - 1);
                            bool haveChanges = false;
                            foreach (var otherPair in AdjacentCells(i, j))
                            {
                                uint        otherMask  = board[otherPair.Item1, otherPair.Item2];
                                LogicResult findResult = sudokuSolver.ClearMask(otherPair.Item1, otherPair.Item2, clearMask);
                                if (findResult == LogicResult.Invalid)
                                {
                                    logicalStepDescription.Clear();
                                    logicalStepDescription.Append($"{CellName(i, j)} with values {MaskToString(mask)} removes the only candidate {bannedVal} from {CellName(otherPair)}");
                                    return(LogicResult.Invalid);
                                }

                                if (findResult == LogicResult.Changed)
                                {
                                    if (!haveChanges)
                                    {
                                        logicalStepDescription.Append($"{CellName((i, j))} having candidates {MaskToString(mask)} removes {bannedVal} from {CellName(otherPair)}");
                                        haveChanges = true;
                                    }
                                    else
                                    {
                                        logicalStepDescription.Append($", {CellName(otherPair)}");
                                    }
                                }
                            }
                            if (haveChanges)
                            {
                                return(LogicResult.Changed);
                            }
                        }
                    }
                    else if (maskValueCount == 3)
                    {
                        int minValue = MinValue(mask);
                        int maxValue = MaxValue(mask);
                        if (maxValue - minValue == 2)
                        {
                            // Three consecutive values means adjacent cells can't be the middle value
                            int  clearValue = minValue + 1;
                            uint clearMask  = ValueMask(clearValue);
                            bool changed    = false;
                            foreach (var otherPair in AdjacentCells(i, j))
                            {
                                LogicResult findResult = sudokuSolver.ClearMask(otherPair.Item1, otherPair.Item2, clearMask);
                                if (findResult == LogicResult.Invalid)
                                {
                                    logicalStepDescription.Clear();
                                    logicalStepDescription.Append($"{CellName(i, j)} with values {MaskToString(mask)} removes the only candidate {clearValue} from {CellName(otherPair)}");
                                    return(LogicResult.Invalid);
                                }

                                if (findResult == LogicResult.Changed)
                                {
                                    if (!changed)
                                    {
                                        logicalStepDescription.Append($"{CellName((i, j))} having candidates {MaskToString(mask)} removes {clearValue} from {CellName(otherPair)}");
                                        changed = true;
                                    }
                                    else
                                    {
                                        logicalStepDescription.Append($", {CellName(otherPair)}");
                                    }
                                }
                            }
                            if (changed)
                            {
                                return(LogicResult.Changed);
                            }
                        }
                    }
                }
            }

            // Look for groups where a particular digit is locked to 2, 3, or 4 places
            // For the case of 2 places, if they are adjacent then neither can be a consecutive digit
            // For the case of 3 places, if they are all adjacent then the center one cannot be a consecutive digit
            // For all cases, any cell that is adjacent to all of them cannot be a consecutive digit
            // That last one should be a generalized version of the first two if we count a cell as adjacent to itself
            var valInstances = new (int, int)[MAX_VALUE];
Esempio n. 2
0
        public override LogicResult StepLogic(SudokuSolver sudokuSolver, StringBuilder logicalStepDescription, bool isBruteForcing)
        {
            var board = sudokuSolver.Board;

            // Look for single cells that can eliminate on its diagonals.
            // Some eliminations can only happen within the same box.
            for (int i = 0; i < HEIGHT; i++)
            {
                for (int j = 0; j < WIDTH; j++)
                {
                    uint mask = board[i, j];
                    if (IsValueSet(mask))
                    {
                        continue;
                    }

                    int valueCount = ValueCount(mask);
                    if (valueCount <= 3)
                    {
                        int minValue = MinValue(mask);
                        int maxValue = MaxValue(mask);
                        if (maxValue - minValue == 2)
                        {
                            // Values 2 apart will always remove the center value, but if there are 3 candidates this only applies to the same box
                            bool haveChanges     = false;
                            int  removeValue     = minValue + 1;
                            uint removeValueMask = ValueMask(removeValue);
                            foreach (var cell in DiagonalCells(i, j, valueCount != 2))
                            {
                                LogicResult findResult = sudokuSolver.ClearMask(cell.Item1, cell.Item2, removeValueMask);
                                if (findResult == LogicResult.Invalid)
                                {
                                    logicalStepDescription.Clear();
                                    logicalStepDescription.Append($"{CellName((i, j))} having candidates {MaskToString(mask)} removes the only candidate {removeValue} from {CellName(cell)}");
                                    return(LogicResult.Invalid);
                                }

                                if (findResult == LogicResult.Changed)
                                {
                                    if (!haveChanges)
                                    {
                                        logicalStepDescription.Append($"{CellName((i, j))} having candidates {MaskToString(mask)} remove {removeValue} from {CellName(cell)}");
                                        haveChanges = true;
                                    }
                                    else
                                    {
                                        logicalStepDescription.Append($", {CellName(cell)}");
                                    }
                                }
                            }
                            if (haveChanges)
                            {
                                return(LogicResult.Changed);
                            }
                        }
                        else if (maxValue - minValue == 1)
                        {
                            // Values 1 apart will always remove both values, but only for diagonals in the same box
                            bool haveChanges     = false;
                            uint removeValueMask = ValueMask(minValue) | ValueMask(maxValue);
                            foreach (var cell in DiagonalCells(i, j, true))
                            {
                                LogicResult findResult = sudokuSolver.ClearMask(cell.Item1, cell.Item2, removeValueMask);
                                if (findResult == LogicResult.Invalid)
                                {
                                    logicalStepDescription.Clear();
                                    logicalStepDescription.Append($"{CellName(i, j)} removes the only candidates {MaskToString(removeValueMask)} from {CellName(cell)}");
                                    return(LogicResult.Invalid);
                                }

                                if (findResult == LogicResult.Changed)
                                {
                                    if (!haveChanges)
                                    {
                                        logicalStepDescription.Append($"{CellName((i, j))} having candidates {minValue}{maxValue} remove those values from {CellName(cell)}");
                                        haveChanges = true;
                                    }
                                    else
                                    {
                                        logicalStepDescription.Append($", {CellName(cell)}");
                                    }
                                }
                            }
                            if (haveChanges)
                            {
                                return(LogicResult.Changed);
                            }
                        }
                    }
                }
            }

            // Look for groups where a particular digit is locked to 2, 3, or 4 places
            // Any cell that is diagonal to all of them cannot be either consecutive digit
            var valInstances = new (int, int)[MAX_VALUE];