Skip to content
Advertisement

Sudoku puzzle test

Trying to write a script that check if Sudoku puzzle is correctly solved. I tried to check every value using 2 cycles and test method and replace value to “0” if test return true but script does not replace all the values and I don’t understand why. Could you please explain why script replaces some values, but others does not?

const doneOrNot = (sudoku) => {
  const copiedSudoku = JSON.parse(JSON.stringify(sudoku));
  console.log(copiedSudoku);
  const testNums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  for (const num in copiedSudoku) {
    for (const newnum in copiedSudoku[num]) {
      const regexp = RegExp(`${testNums[newnum]}`);

      if (regexp.test(copiedSudoku[num])) {
        copiedSudoku[num][newnum] = 0;
      }
    }
  }
  return copiedSudoku;
};

console.log(doneOrNot(
  [[5, 3, 4, 6, 7, 8, 9, 1, 2],
    [6, 7, 2, 1, 9, 5, 3, 4, 8],
    [1, 9, 8, 3, 4, 2, 5, 6, 7],
    [8, 5, 9, 7, 6, 1, 4, 2, 3],
    [4, 2, 6, 8, 5, 3, 7, 9, 1],
    [7, 1, 3, 9, 2, 4, 8, 5, 6],
    [9, 6, 1, 5, 3, 7, 2, 8, 4],
    [2, 8, 7, 4, 1, 9, 6, 3, 5],
    [3, 4, 5, 2, 8, 6, 1, 7, 9]],
));

Advertisement

Answer

but script does not replace all the values and I don’t understand why.

That happens because your code looks for the column number as a value in the current row, but that value may have already been changed to 0, and so there is no match, and the current value will not be replaced by 0.

In your example this happens in the first row, when the inner loop is in its 3rd iteration. Then newnum is equal to 2, at which moment the current sudoku row copiedSudoku[num] looks like this:

[0, 0, 4, 6, 7, 8, 9, 1, 2]

As you see, the previous two iterations of the inner loop placed a zero, but now testNums[newnum] is 3, and that number was already erased, so the test method call will return false, and so that 4 is not cleared.

Issues

  • The main comment on this algorithm is that it should be unnecessary to modify anything in the given Sudoku. The algorithm should just look and calculate.

  • It is overkill to create a regular expression just to find one digit. For that you can use the include array method.

  • The .test RegExp method expects a string as argument, but you pass it an array. This means the array is converted to a comma-separated string, and so it still kinda works, but it certainly is not advised.

  • It is not good practice to use a for..in loop over an array. If you are interested in the values in the array, use a for..of loop.

  • As your function’s name suggests a boolean return value, it should not return the Sudoku, but indicate success with true or false.

Read-only solution

As said, there should be no need to place zeroes. It doesn’t help to reach your goal.

const hasAllDigits = arr =>
    arr.reduce((acc, digit) => acc | (1 << digit), 0) == 0x3FE;

const doneOrNot = (sudoku) => {
    for (let i = 0; i < 9; i+=3) {
        for (let j = 0; j < 3; j++) {
            let k = i + j;
            // Check k-th row
            if (!hasAllDigits(sudoku[k])) return false;
            // Check k-th column
            if (!hasAllDigits(sudoku.map(row => row[k]))) return false;
            // Check k-th 3x3 box
            if (!hasAllDigits(sudoku.slice(j*3, j*3+3)
                  .flatMap(row => row.slice(i, i + 3)))) return false;
        }
    }
    return true;
};

console.log(doneOrNot(
  [[5, 3, 4, 6, 7, 8, 9, 1, 2],
    [6, 7, 2, 1, 9, 5, 3, 4, 8],
    [1, 9, 8, 3, 4, 2, 5, 6, 7],
    [8, 5, 9, 7, 6, 1, 4, 2, 3],
    [4, 2, 6, 8, 5, 3, 7, 9, 1],
    [7, 1, 3, 9, 2, 4, 8, 5, 6],
    [9, 6, 1, 5, 3, 7, 2, 8, 4],
    [2, 8, 7, 4, 1, 9, 6, 3, 5],
    [3, 4, 5, 2, 8, 6, 1, 7, 9]],
));
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement