3

I know that there are a few posts like these before, but they aren't helping me. I am writing a program that can solve a sudoku. I found an algorithm here: http://www.heimetli.ch/ffh/simplifiedsudoku.html . I am trying to write it in java and starting with a console based program. It gets into an infinite loop for some reason, even though I have ways to stop it.

package sudokuSolver;

public class Solver {
static int[][] board;   //teh board
static boolean solved;          //if the sudoku is solved

public static void main(String[] args) throws Exception 
{
    //establish temporary board for now
    final int[][] TUE24JAN = {{0,0,9,0,0,0,8,0,0},
                              {0,1,0,0,2,0,0,3,0},
                              {0,0,0,7,0,8,0,0,0},
                              {2,0,0,0,8,0,0,0,7},
                              {0,3,0,1,0,2,0,4,0},
                              {4,0,0,0,7,0,0,0,5},
                              {0,0,0,6,0,3,0,0,0},
                              {0,8,0,0,9,0,0,7,0},
                              {0,0,6,0,0,0,9,0,0},};

    final int[][] WED25JAN = {{2,5,0,0,0,0,4,0,0},
                              {0,0,3,1,0,0,7,0,0},
                              {0,0,0,0,8,4,0,6,0},
                              {4,0,0,0,0,0,0,8,0},
                              {7,0,0,0,1,0,0,0,4},
                              {0,3,0,0,0,0,0,0,9},
                              {0,9,0,6,5,0,0,0,0},
                              {0,0,1,0,0,9,2,0,0},
                              {0,0,2,0,0,0,0,4,3},};

    board = TUE24JAN;
    solved = false;
    printBoard();

    solve(0,0);
    System.out.println("\n");
    printBoard();
}

public static void solve(int x, int y) throws Exception
{
    //catches the end of the line
    if(y > 8)
    {
        y = 0;
        x++;
    }

    //catches the end of the board
    if(x > 8 || solved)
    {
        solved = true;
        return;
    }
    //put a number in the cell
    for(int i = 1; i < 10; i++)
    {
        if(!inRow(x, i) && !inCol(y, i) && !inBox(x, y, i) && !solved)
        {
            board[x][y] = i;
            solve(x, y+1);
            board[x][y] = 0;
        }
    }
}

//returns if the value is in the specified row
public static boolean inRow(int x, int val)
{
    for(int i = 0; i < 9; i++)
        if(board[x][i] == val)
            return true;
    return false;
}

//returns whether the value is in the specified column
public static boolean inCol(int y, int val)
{
    for(int i = 0; i < 9; i++)
        if(board[i][y] == val)
            return true;
    return false;
}

//returns whether the value fits based
public static boolean inBox(int x, int y, int val)
{
    int row = (x / 3) * 3;
    int col = (y / 3) * 3;
    for(int r = 0; r < 3; r++)
    for(int c = 0; c < 3; c++)
        if(board[row+r][col+c] == val)
            return true;
    return false;
}

public static void printBoard()
{

    for(int i = 0; i < 9; i++)
    {
        if( i % 3 == 0)
            System.out.println("----------------------");
        for(int j = 0; j < 9; j++)
        {
            if(j % 3 == 0)
                System.out.print("|");
            if(board[i][j] < 10 && board[i][j] > 0)
                System.out.print(board[i][j] + " ");
            else
                System.out.print("- ");
        }
        System.out.println("|");
    }
    System.out.print("----------------------\n");
}

}

Edit: It should not clear the cells because when it finally reaches a solution, it changes solved to true which lets it know to not change anymore values. I am not getting a stack overflow error, it just keeps running. I accidentally let it run for an hour and it was still running, it just kept repeating at one point, never hit a solved state and never hit the first recursive sequence.

As for step by step debugging, you can do that? I use eclipse, but if there's a different IDE that allows you to do a line by line run-through, could you tell me?

Sean Lev
  • 121
  • 1
  • 6
  • 2
    Have you tried debugging the program step by step to try to figure out why you get an infinite loop? – assylias May 29 '12 at 17:16
  • @trutheality he has in the solve method near the top `x++;` which would increment the row. I think he needs a `solve(x, y);` at that point though... I haven't tracked through all the code though. – Shaded May 29 '12 at 17:30
  • @Shaded Good catch. That looks like it should work then. Not sure where the infinite recursion is coming from in that case... – trutheality May 29 '12 at 17:43
  • 1
    Sean, are you sure that you're getting stuck in an infinite loop? It looks to me like it could just take a very very long time since you make a recursive call inside of a `for` loop. Are you getting a `Stack Overflow Exception` at any point? – Shaded May 29 '12 at 17:48
  • 1
    One problem is that this clears the cells after it finds the solution. That wouldn't cause infinite recursion though. – trutheality May 29 '12 at 17:52
  • 1
    Also, you don't skip nonempty cells. – trutheality May 29 '12 at 18:24
  • @trutheality, I love how your second comment was hidden, but it brought attention to an important issue. Now I don't have an infinite recursion issue. Unfortunately, it still doesn't work. – Sean Lev May 30 '12 at 00:08
  • @SeanLev Re. your comment in the edit about not clearing solved cells -- it will clear them because there is no check for `solved` after the call to `solve`. Zecas fixed that in the answer. – trutheality May 30 '12 at 18:35

1 Answers1

1

The code sample above doesn't make use of the full potential of recursion mainly do to the global solved variable. I didn't made a solution from scratch but rather tried to fixed what you presented so that you can see the differences. Please comment if you have any doubts. Without debugging but with a little logging and some nice comments stated above i came up with:

package sudoku;

public class Solver {
    static int[][] board; //teh board
    static boolean solved; //if the sudoku is solved

    public static void main(String[] args) throws Exception {

        //establish temporary board for now
        final int[][] TUE24JAN =
            {
                {0, 0, 9, 0, 0, 0, 8, 0, 0},
                {0, 1, 0, 0, 2, 0, 0, 3, 0},
                {0, 0, 0, 7, 0, 8, 0, 0, 0},

                {2, 0, 0, 0, 8, 0, 0, 0, 7},
                {0, 3, 0, 1, 0, 2, 0, 4, 0},
                {4, 0, 0, 0, 7, 0, 0, 0, 5},

                {0, 0, 0, 6, 0, 3, 0, 0, 0},
                {0, 8, 0, 0, 9, 0, 0, 7, 0},
                {0, 0, 6, 0, 0, 0, 9, 0, 0},
            };

        final int[][] WED25JAN =
            {
                {2, 5, 0, 0, 0, 0, 4, 0, 0},
                {0, 0, 3, 1, 0, 0, 7, 0, 0},
                {0, 0, 0, 0, 8, 4, 0, 6, 0},
                {4, 0, 0, 0, 0, 0, 0, 8, 0},
                {7, 0, 0, 0, 1, 0, 0, 0, 4},
                {0, 3, 0, 0, 0, 0, 0, 0, 9},
                {0, 9, 0, 6, 5, 0, 0, 0, 0},
                {0, 0, 1, 0, 0, 9, 2, 0, 0},
                {0, 0, 2, 0, 0, 0, 0, 4, 3},
            };

        board = TUE24JAN;
        solved = false;
        printBoard();

        solve(0, 0);
        System.out.println("\n");
        printBoard();
    } // end method main

    public static void solve(int x, int y) throws Exception {

        //printBoard();
        System.out.println(x + " : " + y);

        //catches the end of the line
        if (y > 8) {
            y = 0;
            x++;
        }

        //catches the end of the board
        if ((x > 8) || solved) {
            solved = true;
            return;
        }

        //put a number in the cell
        for (int i = 1; i < 10; i++) {

            if ((board[x][y] == 0)) { // cell to be filled

                if (!inRow(x, i) && !inCol(y, i) && !inBox(x, y, i)) { // can use number

                    board[x][y] = i;

                    solve(x, y + 1);

                    if (solved) {
                        return;
                    }

                    board[x][y] = 0;

                }
            }
            else {
                solve(x, y + 1);

                return;

            }
        } // end for
    } // end method solve

    //returns if the value is in the specified row
    public static boolean inRow(int x, int val) {

        for (int i = 0; i < 9; i++) {

            if (board[x][i] == val) {
                return true;
            }
        }

        return false;
    }

    //returns whether the value is in the specified column
    public static boolean inCol(int y, int val) {

        for (int i = 0; i < 9; i++) {

            if (board[i][y] == val) {
                return true;
            }
        }

        return false;
    }

    //returns whether the value fits based
    public static boolean inBox(int x, int y, int val) {
        int row = (x / 3) * 3;
        int col = (y / 3) * 3;

        for (int r = 0; r < 3; r++) {

            for (int c = 0; c < 3; c++) {

                if (board[row + r][col + c] == val) {
                    return true;
                }
            }
        }

        return false;
    }

    public static void printBoard() {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < 9; i++) {

            if ((i % 3) == 0) {
                sb.append("----------------------\n");
            }

            for (int j = 0; j < 9; j++) {

                if ((j % 3) == 0) {
                    sb.append("|");
                }

                if ((board[i][j] < 10) && (board[i][j] > 0)) {
                    sb.append(board[i][j] + " ");
                }
                else {
                    sb.append("- ");
                }
            }

            sb.append("|\n");
        }

        sb.append("----------------------\n");
        System.out.println(sb.toString());

        /*try {
            Thread.sleep(100);
        }
        catch (InterruptedException e) {

            // TODO Auto-generated catch block
            e.printStackTrace();
        }*/
    } // end method printBoard
} // end class Solver
Zecas
  • 647
  • 4
  • 23
  • 1
    +1 but you could have just said, "after calling solve() in your for loop check the solved condition and return if true"...I spent a few minutes reading your code and comparing to try and figure out what exactly you did different. – Kevin May 30 '12 at 14:38