0

I am trying to implement a Sudoku solver using Java. This is the code I've written as of now. If I try to run it, it goes on to an endless loop that keeps on printing the first row of the Sudoku board, and that too with an incorrect solution. I guess I'm implementing backtracking the incorrect way over here. I think I am printing the final and wrong as well, as only the first row is printed every time. Can someone please help me fix my code and tell me as to where I am going wrong?

public static void display(int[][] board) {
    for(int[] arr : board) {
        System.out.println(Arrays.toString(arr));
        return;
    }
}

public static boolean isSafe(int[][] board, int row, int col, int i) {
    //check row
    for(int a=0; a<board.length; a++) {
        if(board[a][col]==i) {
            return false;
        }
    }
    //check col
    for(int b=0; b<board.length; b++) {
        if(board[row][b]==i) {
            return false;
        }
    }
    //check cell
    int strow = row-(row%3);
    int stcol = col-(col%3);
    
    for(int x=strow; x<strow+3; x++) {
        for(int y=stcol; y<stcol+3; y++) {
            if(board[x][y]==i) {
                return false;
            }
        }
    }
    return true;
}

public static void sudoku(int[][] board, int row, int col) {
    if(row==board.length) {
        display(board);
        System.out.println();
        return; //modify this to print ans
    }
    if(col==board.length) {
        sudoku(board, row+1, 0);
        return;
    }
    if(board[row][col]==0) {
        for(int i=1; i<=9; i++) {
            if(isSafe(board, row, col, i)) {
                board[row][col]=i;
                sudoku(board, row, col+1);
                board[row][col]=0;
            }
        }
    }
    sudoku(board, row, col+1);
}

public static void main(String args[]) {
    int[][] board= 
           { {3, 0, 6, 5, 0, 8, 4, 0, 0}, 
             {5, 2, 0, 0, 0, 0, 0, 0, 0}, 
             {0, 8, 7, 0, 0, 0, 0, 3, 1}, 
             {0, 0, 3, 0, 1, 0, 0, 8, 0}, 
             {9, 0, 0, 8, 6, 3, 0, 0, 5}, 
             {0, 5, 0, 0, 9, 0, 6, 0, 0}, 
             {1, 3, 0, 0, 0, 0, 2, 5, 0}, 
             {0, 0, 0, 0, 0, 0, 0, 7, 4}, 
             {0, 0, 5, 2, 0, 6, 3, 0, 0} };
    sudoku(board, 0, 0);
}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
ASC
  • 1
  • 1
  • row-row%3 is to locate the first cell of each 3x3 grid. I have then iterated through the grid to check if the number in consideration is present in the grid. – ASC Feb 23 '22 at 14:23
  • If I'm at the index 4,3. `row-row%3` would give me 3, 6, which is the first element in the rightmost grid (middle row) of a sudoku board. I can use these values to iterate through the grid. While if I use row/3, it'd give me a different answer. – ASC Feb 23 '22 at 14:37
  • so is 0 is the cells where we need to do the filling? – Rohith V Feb 23 '22 at 14:39
  • @user16320675 And thanks a lot for the hint, I'm trying to change my return type of sudoku method to boolean. I'll probably get the correct output then. I will let you know once I get there. Though, I was wondering, is there any way to get the desired output without changing the return type of the sudoku method, ie, keeping it as void and doing maybe something? – ASC Feb 23 '22 at 14:39
  • @RohithV yes, 0s are basically empty cells of the sudoku board, which are to be filled. – ASC Feb 23 '22 at 14:42
  • Understood and added my logic – Rohith V Feb 23 '22 at 14:57
  • @user16320675 `row / 3` is nothing like `row - (row % 3)`. It's completely different, I don't know why you would think that – Ivo Feb 24 '22 at 07:59

1 Answers1

0

A bactracking algorithm can be applied here and the problem happens in your sudoku method. First of all we can just pass the board and we don't want to pass the row and col. We can just pass the board and the just traverse through each and every cell. Only consider those cells that are 0's. We don't want to consider any other cells as 0"s are the cells we are interested in. Now if we see a cell which is 0, we try to find all the different possibilites from 1 to 9 which can fit in that cell and apply the 'isSafe()` logic which will just do the same check. And we backtrack and continue with our checking.

public static void display(int[][] board) {
    for(int[] arr : board) {
        System.out.println(Arrays.toString(arr));
    }
}

public static boolean isSafe(int[][] board, int row, int col, int i) {
    //check row
    for(int a=0; a<board.length; a++) {
        if(board[a][col]==i) {
            return false;
        }
    }
    //check col
    for(int b=0; b<board.length; b++) {
        if(board[row][b]==i) {
            return false;
        }
    }
    //check cell
    int strow = row-(row%3);
    int stcol = col-(col%3);
    
    for(int x=strow; x<strow+3; x++) {
        for(int y=stcol; y<stcol+3; y++) {
            if(board[x][y]==i) {
                return false;
            }
        }
    }
    return true;
}

public static boolean sudoku(int [][] board) {
        for (int i=0; i<9; i++) {
            for (int j=0; j<9; j++) {
                int current = board[i][j];
                if (current == 0) {
                    for (int ch = 1; ch <= 9; ch++) {
                        if (isSafe(board, i, j, ch)) {
                            board[i][j] = ch;
                            if (sudoku(board)) {
                                return true;
                            }
                            board[i][j] = 0;
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }
public static void main(String args[]) {
    int[][] board= 
           { {3, 0, 6, 5, 0, 8, 4, 0, 0}, 
             {5, 2, 0, 0, 0, 0, 0, 0, 0}, 
             {0, 8, 7, 0, 0, 0, 0, 3, 1}, 
             {0, 0, 3, 0, 1, 0, 0, 8, 0}, 
             {9, 0, 0, 8, 6, 3, 0, 0, 5}, 
             {0, 5, 0, 0, 9, 0, 6, 0, 0}, 
             {1, 3, 0, 0, 0, 0, 2, 5, 0}, 
             {0, 0, 0, 0, 0, 0, 0, 7, 4}, 
             {0, 0, 5, 2, 0, 6, 3, 0, 0} };
    sudoku(board);
    display(board);
    
    }

Here is the output after the change

[3, 1, 6, 5, 7, 8, 4, 9, 2]
[5, 2, 9, 1, 3, 4, 7, 6, 8]
[4, 8, 7, 6, 2, 9, 5, 3, 1]
[2, 6, 3, 4, 1, 5, 9, 8, 7]
[9, 7, 4, 8, 6, 3, 1, 2, 5]
[8, 5, 1, 7, 9, 2, 6, 4, 3]
[1, 3, 8, 9, 4, 7, 2, 5, 6]
[6, 9, 2, 3, 5, 1, 8, 7, 4]
[7, 4, 5, 2, 8, 6, 3, 1, 9]

Note : I changed the return type to true or false inorder to make sure that a particular cell can be filled with a number say x. If its possible we return true and continue with the next cell which is 0, other wise we backtrack and check for other possibilities.

Updates : The only change you are missing is an else block at the very end because even if the cell is 0 or any other number you are doing recursion sudoku(board, row, col+1);. So just enclose that statement in the else block and will give the desired output.

Code change without changing the return type:

public static void display(int[][] board) {
    for(int[] arr : board) {
        System.out.println(Arrays.toString(arr));
    }
}

public static boolean isSafe(int[][] board, int row, int col, int i) {
    //check row
    for(int a=0; a<board.length; a++) {
        if(board[a][col]==i) {
            return false;
        }
    }
    //check col
    for(int b=0; b<board.length; b++) {
        if(board[row][b]==i) {
            return false;
        }
    }
    //check cell
    int strow = row-(row%3);
    int stcol = col-(col%3);
    
    for(int x=strow; x<strow+3; x++) {
        for(int y=stcol; y<stcol+3; y++) {
            if(board[x][y]==i) {
                return false;
            }
        }
    }
    return true;
}

public static void sudoku(int[][] board, int row, int col) {
    if(row==board.length) {
        display(board);
        System.out.println();
        return; //modify this to print ans
    }
    if(col==board.length) {
        sudoku(board, row+1, 0);
        return;
    }
    if(board[row][col]==0) {
        for(int i=1; i<=9; i++) {
            if(isSafe(board, row, col, i)) {
                board[row][col]=i;
                sudoku(board, row, col+1);
                board[row][col]=0;
            }
        }
    }
    else
        sudoku(board, row, col+1);
}
public static void main(String args[]) {
    int[][] board= 
           { {3, 0, 6, 5, 0, 8, 4, 0, 0}, 
             {5, 2, 0, 0, 0, 0, 0, 0, 0}, 
             {0, 8, 7, 0, 0, 0, 0, 3, 1}, 
             {0, 0, 3, 0, 1, 0, 0, 8, 0}, 
             {9, 0, 0, 8, 6, 3, 0, 0, 5}, 
             {0, 5, 0, 0, 9, 0, 6, 0, 0}, 
             {1, 3, 0, 0, 0, 0, 2, 5, 0}, 
             {0, 0, 0, 0, 0, 0, 0, 7, 4}, 
             {0, 0, 5, 2, 0, 6, 3, 0, 0} };
    sudoku(board, 0, 0);
    
    }

Output :

[3, 1, 6, 5, 7, 8, 4, 9, 2]
[5, 2, 9, 1, 3, 4, 7, 6, 8]
[4, 8, 7, 6, 2, 9, 5, 3, 1]
[2, 6, 3, 4, 1, 5, 9, 8, 7]
[9, 7, 4, 8, 6, 3, 1, 2, 5]
[8, 5, 1, 7, 9, 2, 6, 4, 3]
[1, 3, 8, 9, 4, 7, 2, 5, 6]
[6, 9, 2, 3, 5, 1, 8, 7, 4]
[7, 4, 5, 2, 8, 6, 3, 1, 9]
Rohith V
  • 1,089
  • 1
  • 9
  • 23
  • Thanks a lot, I really appreciate your help. I had 2 doubts that I thought I'd ask you. 1. the `return false` in the sudoku method would be triggered if there's no possible solution for the present cell right? 2. Is there any way by which we can solve this out without changing the return type of sudoku method from void to boolean? – ASC Feb 23 '22 at 15:06
  • yes, I will try without changing the return type. Does my solution is giving you the right answer? – Rohith V Feb 24 '22 at 07:07
  • does the updated code helps you in any way @ASC – Rohith V Feb 25 '22 at 12:11