0

I've written the following recursive code, for solving a sudoku puzzle.

grid: a global matrix, representing the puzzle.
possible function: returns true or false for a given number and location
solve: a recursion that fills the grid.

However I have a bug, how can I debug it without being trapped in an endless loop.

Is there some sort of force exit? Can you spot the bug?

let grid = [
  [5, 3, 0, 0, 7, 0, 0, 0, 0],
  [6, 0, 0, 1, 9, 5, 0, 0, 0],
  [0, 9, 8, 0, 0, 0, 0, 6, 0],
  [8, 0, 0, 0, 6, 0, 0, 0, 3],
  [4, 0, 0, 8, 0, 3, 0, 0, 1],
  [7, 0, 0, 0, 2, 0, 0, 0, 6],
  [0, 6, 0, 0, 0, 0, 2, 8, 0],
  [0, 0, 0, 4, 1, 9, 0, 0, 5],
  [0, 0, 0, 0, 8, 0, 0, 7, 9]];

function possible(x, y, n) {
  for (let i = 0; i < 9; i++) {
    if (grid[y][i] === n) {
      return false
    }
  }
  for (let i = 0; i < 9; i++) {
    if (grid[i][x] === n) {
      return false
    }
  }
  let x0 = Math.floor(x / 3) * 3;
  let y0 = Math.floor(y / 3) * 3;
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      if (grid[y0 + i][x0 + j] === n) {
        return false
      }
    }
  }
  return true;
}

function solve() {
  for (let y = 0; y < 9; y++) {
    for (let x = 0; x < 9; x++) {
      if (grid[y][x] === 0) {
        for (let n = 1; n < 10; n++) {
          if(possible(y,x,n)){
            grid[y][x] = n;
            solve();
            grid[y][x] = 0;
          }
        }
        return;
      }
    }
  }
}

solve();
Roni Gadot
  • 437
  • 2
  • 19
  • 30
  • 2
    There's [`debugger`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger)? Depending on your environment. – George Mar 05 '20 at 15:28
  • 1
    I don't think there's faulty recursion here, but just a very large problem space to recurse through. You may need a less brute-force algorithm. – AKX Mar 05 '20 at 15:29
  • 2
    Well actually, there should probably be an exit condition when the grid is fully solved. :) Otherwise the `grid[y][x]` line will undo any solution found. – AKX Mar 05 '20 at 15:31
  • @Adam They're also asking if we can spot the bug. – AKX Mar 05 '20 at 15:32
  • In order to debug, I'd log some evidence of convergence. Maybe, at the top of solve, log a count of zeros in the matrix. No zeros is the condition to pop out of the recursion anyway, right? – danh Mar 05 '20 at 15:33
  • 1
    Does this answer your question? [How can I debug my JavaScript code?](https://stackoverflow.com/questions/988363/how-can-i-debug-my-javascript-code) – Liam Mar 05 '20 at 15:56

3 Answers3

1

Standard debugging techniques should generally work here. Learn to use your environment's debugger. For example, if this is running in your browser, you should be able to set a breakpoint in your browser's developer tools, and walk through the code line by line to try to understand what's happening.

Recursion always requires there to be some base condition that causes the recursion to end. In your case, if there are no unsolved squares you could indicate that by returning true, and then pass that "success" status up the call chain.

Also, your call to possible has switched the expected argument positions of x and y.

function solve() {
  for (let y = 0; y < 9; y++) {
    for (let x = 0; x < 9; x++) {
      if (grid[y][x] === 0) {
        for (let n = 1; n < 10; n++) {
          if(possible(x,y,n)){
            grid[y][x] = n;
            var solved = solve();
            if(solved) {
                return true;
            }
            grid[y][x] = 0;
          }
        }
        return false;
      }
    }
  }
  return true; // We didn't find any unsolved squares.
}

let grid = [
  [5, 3, 0, 0, 7, 0, 0, 0, 0],
  [6, 0, 0, 1, 9, 5, 0, 0, 0],
  [0, 9, 8, 0, 0, 0, 0, 6, 0],
  [8, 0, 0, 0, 6, 0, 0, 0, 3],
  [4, 0, 0, 8, 0, 3, 0, 0, 1],
  [7, 0, 0, 0, 2, 0, 0, 0, 6],
  [0, 6, 0, 0, 0, 0, 2, 8, 0],
  [0, 0, 0, 4, 1, 9, 0, 0, 5],
  [0, 0, 0, 0, 8, 0, 0, 7, 9]];

function possible(x, y, n) {
  for (let i = 0; i < 9; i++) {
    if (grid[y][i] === n) {
      return false
    }
  }
  for (let i = 0; i < 9; i++) {
    if (grid[i][x] === n) {
      return false
    }
  }
  let x0 = Math.floor(x / 3) * 3;
  let y0 = Math.floor(y / 3) * 3;
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      if (grid[y0 + i][x0 + j] === n) {
        return false
      }
    }
  }
  return true;
}

function solve() {
  // find the first unsolved square.
  for (let y = 0; y < 9; y++) {
for (let x = 0; x < 9; x++) {
  if (grid[y][x] === 0) {
    // try every possible number in that square
    for (let n = 1; n < 10; n++) {
      if(possible(x,y,n)){
        grid[y][x] = n;
        var solved = solve();
        // if this led to a valid board, leave the board as-is and return success.
        if(solved) {
            return true;
        }
        grid[y][x] = 0;
      }
    }
    return false;
  }
}
  }
  console.log("all squares are solved");
  return true; // We didn't find any unsolved squares.
}

console.log(solve());
console.log(grid);
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
0

You Can Use Google Chrome Debug For Java Script Click to see

For Example You Can Creat Break Point In That: Click to see

Or Set Watch For Vars :Click For see

For Use It Press F12 And Select "Source" And Find Your JS File For Debug


For See More Go And Read https://www.w3schools.com/js/js_debugging.asp

SecretAgentMan
  • 2,856
  • 7
  • 21
  • 41
0

When writing recursive functions, it is very possible that it may end up calling infinite with a stack overflow if it is repeating exactly the same again and again.

In your code, you are calling solve function which will loop from the start of grid up to the end. And again solve function called itself which caused to loop all over again and again... so it will never stops until stack overflow happens.

In below code, I am sure I did not solve your sudoku puzzle but at least it shows how to avoid calling infinitely by telling where to restart the loop so that it will not be looping all over again. Again, I did not solve your sudoku puzzle .. just a demo of recursive nature. I also added console.log so that you will be able to see how solve function is called with different parameters each time.

function solve(ystart, xstart) {
  console.log('solve(', ystart + ',' + xstart, ')');
  for (let y = ystart; y < 9; y++) {
    for (let x = xstart; x < 9; x++) {
      if (grid[y][x] === 0) {
        for (let n = 1; n < 10; n++) {
          if(possible(y,x,n)){
            grid[y][x] = n;
            solve(y, x);
            grid[y][x] = 0;
          }
        }
        return;
      }
    }
  }
}

solve(0, 0);
Shwe
  • 455
  • 5
  • 11
  • What you're proposing could be an optimization for a valid solution, but it doesn't solve any logical problem with the original. The check for `if(grid[y][x] === 0)` should cause the next call down the stack to skip over the square that was changed by the call further up the stack, because it had already changed `grid[y][x]` to a non-zero value. – StriplingWarrior Mar 05 '20 at 16:13