Csci 210 Lab: Sudoku

(Laura Toma adapted from Eric Chown)

Overview

In this lab we are going to use search to solve the game Sudoku. In Sudoku the goal is to complete a grid of numbers. When finished each row and column must include all of the numbers 1 to 9. In addition, there are 9 3x3 subgrids on the board that must also contain the numbers 1 through 9. In a typical game a few of the squares are filled in for you. The more that are filled in the easier it is to complete the grid.

Your program should consist of at least three classes. A main class, a board class, and a move class. We'll describe these in turn.

The main class.

The main class will run the search and display the game. The search will run so fast that you will probably want to slow it down so you can see it working. An easy way to do this is with the System.timeMillis() command which returns the current time. For example, if I wanted to pause the program for 50 milliseconds I'd do this:
long t = System.timeMillis();
while (System.timeMillis() - t < 50) {}
A nice addition to the program would be to include a Slider so that the user can control the speed.

Your board should be easy to read. Numbers that are given in the file should be easily distinguished from those the program figures out (e.g. use different colors). It is also nice to distinguish the various sectors of the board (chunks of 9 squares).

For the search itself we will want to use depth-first search. This is because the space required for breadth-first search is too large for a computer (the branching factor of the search is 9, with a depth of about 70 for a typical game). We're going to use a slight variant of the depth-first search that we have been talking about. In this version you only have one copy of the state which you continually modify. The trick is to figure out what moves you have made, what move comes next, and when you have to backtrack, how to do that. To figure out the moves you have made we'll use a Stack. Everytime you make a move (fill in a square) you simply push the move onto the Stack (Move, therefore, is a triple of row, col, number). To undo a move you pop it from the Stack. Since we're using brute-force search determining the next move at that point is simple. Let's say you popped off the Move (3, 5, 7). Our strategy will be to simply try moves starting at 1 and going up to 9. So you would next try (3, 5, 8). Your Board class should take care of the details.

The Board class

Your Board constructor should take at least a filename containing a file that stores the initial situation. Optionally you might have it also take a board size (for variant games). It should initialize the board after reading in the initialization information. More on that later. Some other methods you will need:
Move nextMove(int startNumber)
takes the current board and gets the next move (when possible). Returns the Move. If no legal move is available at the next square it returns an empty Move (0, 0, 0). The empty Move is a signal to your main class that it should backtrack (by popping the stack).

To figure out the next square to move to you can use any pattern, e.g. just start at the top left corner and search for the first open square moving left to right and top to bottom. Once you find an open square you must check to see if a legal number can be put onto that square. The legal number must be at least of value "startNumber". When an empty Move is returned that is a signal for the main class to backtrack (pop the Stack). The parameter specifies what to start with (normally 1, but when backtracking can be larger).

boolean isDone()
returns true when the Board has finished.
boolean moveOk(Move move)
checks if putting the value into the corresponding square is legal. This is really the crucial function for speed. It is paramount that you do this as efficiently as possible.

I also suggest you have getters and setters for individual board squares.

The Move class

This class is just a storage class for three ints (row, col, value). It should have appropriate getters.

Reading

To read the board from a file you will use a Scanner class. Reading with the Scanner class is pretty straightforward. Here's some code that would read in a file containing integers.
    public void readBoard(String fileName) {
        File f = new File(fileName);
        Scanner sc = null;
        try {
            sc = new Scanner(f);
        } catch (FileNotFoundException e) {
            System.err.println("File not found!");
            System.exit(0);
        }
        while (sc.hasNextInt()) {
            int nextOne = sc.nextInt();
        }
    }
In a nutshell, the code creates a new File, then opens the file up with the Scanner class. Because this process is fraught with error it is checked for an exception to make sure everything went ok. Once it has been opened, then the Scanner can basically act as an interator.

Comments, Suggestions

The main part of this problem is to understand the search for filling the board. Start by thinking of Sudoku as searching in the space of possible moves for a square. The main class keeps track of what has been tried before---with a stack. The stack is in the main class, and is initially empty. The Board class finds a new move and returns it. The Board has no stack. The stack will tell the main class with what argument to call board.nextMove. Basically, if it calls it with argument 6, this means: all moves smaller than 6 have been tried before (and did not work), so don't bother. Think of this interaction.

Start with a pre-defined board and fill it up with convenient values. Then find the solution. Only after you tested this, add the scanner, and the functionality that the board can be read from a file.