Я пытаюсь создать правильную судоку.

Я создал 3 метода для проверки чисел и один для создания судоку.

createSudoku() пытается создать судоку.

colContainsNumber(): проверьте, содержит ли столбец случайное число, которое я сгенерировал

rowContainsNumber(): То же, что и colContainsNumber, только для строки.

squareContainsNumber(): проверьте, содержит ли блок случайное число.

В createSudoku() я генерирую случайное число, а с помощью цикла while я продолжаю генерировать новое число до тех пор, пока ни один из «содержащих методов» не вернет true (true для «да, число уже находится в строке и т. д.)

Использование всех методов для создания судоку работает. (Например, если я использую только rowContainsNumber, я получу судоку, где ни одна строка не содержит одинаковое число и т. д.)

Но если я использую все три метода вместе, страница не будет отвечать.

Я попытался изменить порядок методов, которые я вызываю в цикле while, но не более того, потому что методы работают нормально, если я вызываю только один из них.

function createSudoku() {
    var sudoku = [
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]
    ];
    for (var i = 0; i < 9; i++) {
        for (var j = 0; j < 9; j++) {
            //generate a random number between 1 and 9
            var randomNumber = Math.floor((Math.random() * 9) + 1);

            /*Keep generate a random number, until the square doesn't contain 
            the number. This is the loop where I'm supposed to use all three 
            Methods (colContains-, rowContains- and squareContainsNumber) but 
            the page doesn't respond if I use all three of them. If I only use 
            one like you can see now, the generation works fine*/

            while (squareContainsNumber(sudoku, i, j, randomNumber)) {
                randomNumber = Math.floor((Math.random() * 9) + 1);

            }
            sudoku[i][j] = randomNumber;
            solvedSudoku[i][j] = randomNumber;
        }
    }
    return sudoku;
}

function rowContainsNumber(sudoku, col, number) {
    for (var i = 0; i < 9; i++) {
        if (sudoku[col][i] == number) {
            return true;
        }
    }
    return false;
}

function colContainsNumber(sudoku, row, number) {
    for (var i = 0; i < 9; i++) {
        if (sudoku[i][row] == number) {
            return true;
        }
    }
    return false;
}

function squareContainsNumber(sudoku, col, row, number)
{
    var minRow, maxRow, minCol, maxCol = 0;
    //Check which column the loop is in, then set the min and max column so I 
    //can get the range of the block
    switch (col) {
        case 0:
        case 1:
        case 2:
            minCol = 0;
            maxCol = 2;
            break;
        case 3:
        case 4:
        case 5:
            minCol = 3;
            maxCol = 5;
            break;
        case 6:
        case 7:
        case 8:
            minCol = 6;
            maxCol = 8;
            break;
        default:
            break;
    }
    //Check which row the loop is in, then set the min and max row so I 
    //can get the range of the block
    switch (row) {
        case 0:
        case 1:
        case 2:
            minRow = 0;
            maxRow = 2;
            break;
        case 3:
        case 4:
        case 5:
            minRow = 3;
            maxRow = 5;
            break;
        case 6:
        case 7:
        case 8:
            minRow = 6;
            maxRow = 8;
            break;
        default:
            break;
    }

   //loop through the square and check If the square contains the random number
    for (var i = minRow; i <= maxRow; i++) {
        for (var j = minCol; j <= maxCol; j++) {
            if (sudoku[i][j] == number)
                return true;
        }
    }

    return false;
}

Ожидаемый результат будет правильной судоку, в которой ни одна строка, ни один столбец и ни один квадрат не содержат одинаковое число.

Но, как я уже сказал, страница просто не отвечает, вероятно, потому, что цикл while занимает слишком много времени.

0
Lars 28 Окт 2019 в 11:37
Я подозреваю, что может потребоваться откат, чтобы избежать создания недопустимого судоку, который из-за того, что ячейкам уже присвоено значение, нельзя исправить, изменив значение следующего пустого квадрата. Вы изучали существующие алгоритмы для создания (решенной) доски судоку?
 – 
traktor
28 Окт 2019 в 12:20
Я пытался найти какой-нибудь алгоритм обратной трассировки, но многие из них работают с «доской судоку», в которой уже есть значения, которых нет у меня.
 – 
Lars
28 Окт 2019 в 13:29

1 ответ

Алгоритм случайного выбора значения ячейки судоку на основе выполнения ограничений, заключающихся в том, чтобы не дублировать существующую цифру в строке, столбце или поле, является неполным. Можно следовать ограничениям, но все же обнаружить, что в пустой ячейке ранее все цифры от 1 до 9 были распределены как соседи в строке, ячейке или поле.

Когда размещенный код достигает этого состояния, он пытается сгенерировать новую случайную цифру для головоломки, не проверяя, возможно ли это. Поскольку это невозможно, страница зависает, поскольку она продолжает пытаться ввести другую случайную цифру, которая не сработает.

Этот фрагмент кода проверяет, что дома (строка, столбец или поле) пустой ячейки действительно содержат неиспользуемую цифру, и повторяет попытку головоломки, если это не так:

var attempts = 0;

function puzzle( sudoku) {
    ++attempts;

    function House() {
        this.index = Object.create(null);
    }
    House.prototype.add = function( n) {
            if( this.index[n]) {
                return 0;
            }
            this.index[n] = true;
            return n;
        };
    
    let houses = [];
    for( var i = 0; i < 27; ++i) {
        houses [i] = new House();
    }
    let rowOf = index =>  Math.floor(index/9);
    let colOf = index =>  index%9;
    let boxOf = index =>  3 * Math.floor( rowOf( index)/3) + Math.floor( colOf( index)/3);
    
    function randomDigit( index) {
        let rowHouse = houses[ rowOf( index)];
        let colHouse = houses[ 9 + colOf(index)];
        let boxHouse = houses[ 18 + boxOf(index)];
        let domain = [1,2,3,4,5,6,7,8,9].reduce( function( array, digit) {
            if( !(rowHouse.index[ digit] || colHouse.index[digit] || boxHouse.index[ digit])) {
                array.push( digit);
            }
            return array;
        }, []);
        let digit = domain.length ? domain[ Math.floor( domain.length * Math.random())] : 0;
        if( digit) {
            rowHouse.add(digit);
            colHouse.add(digit);
            boxHouse.add(digit);
        }
        return digit;
    }

    var sudoku = [];
    for( var index = 0 ; index < 81; ++index) {
        let digit = randomDigit( index);
        if( digit == 0) {
            break;
        }
        sudoku[ index] = digit;
    }

    if( sudoku.length == 81) {
        return sudoku;
    }
    return puzzle();
}

const sudoku = puzzle();

console.log( "Attempts = " + attempts);
if( sudoku.length < 81) {
    sudoku.push('x');
    console.log("invalid sudoku");
}
for( let i = 0; i < 81; i += 9) {
    console.log( sudoku.slice( i, i+9).toString());
}

Запуск сниппета показывает, что для получения результата может потребоваться большое количество попыток.

Беглый взгляд в Интернете показывает, что это может быть необычный метод создания завершенной головоломки, и без дополнительного опыта я не могу комментировать его полезность.

0
traktor 29 Окт 2019 в 10:51