Click here to Skip to main content
Click here to Skip to main content

Sudoku Game in Java

By , 18 Apr 2012
Rate this:
Please Sign up or sign in to vote.

sudokuinjava/Sudoku.jpg

Introduction

This article is on the implementation of a Sudoku game in Java. This version includes an intuitive interface with the ability to use help and to check for errors. Turning on help will mark all possible fields for the selected number. After checking for errors, the program marks valid fields green and invalid fields red. The rules used in this implementation are as follows:

  • An integer may only appear once in ...
    • ... the same row.
    • ... the same column.
    • ... the same 3x3 region.
  • A game has only one solution.

Implementation

Model

The most important part of this application is in the Game class, which includes the following functionality:

  • Generate a new solution;
  • Generate a new game from a solution;
  • Keep track of user input;
  • Check user input against generated solution;
  • Keep track of selected number;
  • Keep track of help is on or off.

Because the Game class extends Observable, it can and does notify observers when certain changes have been performed. This particular application contains two observers, ButtonPanel and SudokuPanel. When the Game class executes setChanged() followed by notifyObservers(...), the observers will execute their update(...) method.

Besides the Game class, the model consists of an enumeration called UpdateAction which will tell observers what kind of update has taken place.

Generate Solution

Before we can start generating a game, we must first generate a solution. This is achieved by the method below, which needs to be called by the user as generateSudoku(new int[9][9], 0). The following steps are taken:

  1. Check if a solution is found.
    • Found -> solution is returned.
    • Not found -> continue.
  2. X (of current field) is found by finding the remainder of the division of the current index by the count of fields in a row using the modulo operation.
  3. Y (of current field) is found by dividing the current index by the count of fields in a row.
  4. An ArrayList is filled with the numbers 1 to 9 and shuffled. Shuffling is important because otherwise you always get the same solution.
  5. As long as there are numbers in the ArrayList, the following will be executed:
    1. The next possible number is obtained by the method getNextPossibleNumber(int[][], int, int, List<Integer>), which will be explained later on. If there's no next possible number (return value of -1), null is returned.
    2. Found number is placed on the current location.
    3. The method is recursively called with an increase of the index, and the returning value is stored in a variable.
    4. If this variable is not null, it is returned; otherwise, the current location is put back to 0 (meaning the field is a blank).
  6. null is returned. This part will never be reached, hopefully.
private int[][] generateSolution(int[][] game, int index) {
    if (index > 80)
        return game;

    int x = index % 9;
    int y = index / 9;

    List<Integer> numbers = new ArrayList<Integer>();
    for (int i = 1; i <= 9; i++)
        numbers.add(i);
    Collections.shuffle(numbers);

    while (numbers.size() > 0) {
        int number = getNextPossibleNumber(game, x, y, numbers);
        if (number == -1)
            return null;

        game[y][x] = number;
        int[][] tmpGame = generateSolution(game, index + 1);
        if (tmpGame != null)
            return tmpGame;
        game[y][x] = 0;
    }

    return null;
}

As previously said, the method getNextPossibleNumber(int[][], int, int, List<Integer>) is used for obtaining the next possible number. It takes a number from the list and checks whether it is possible at the given x and y position in the given game. When found possible, the number is returned. If the list is empty and thus no number is possible at this location, -1 is returned.

private int getNextPossibleNumber(int[][] game, int x, int y, List<Integer> numbers) {
    while (numbers.size() > 0) {
        int number = numbers.remove(0);
        if (isPossibleX(game, y, number)
                && isPossibleY(game, x, number)
                && isPossibleBlock(game, x, y, number))
            return number;
    }
    return -1;
}

Generate Game

Generating a game is simply achieved by constantly removing a random field and making sure the game is still valid. Valid means there is only one solution. This is achieved by the methods below. The user should call the first method, which uses the second method. I will describe the steps again.

  1. A list is filled with all possible positions.
  2. The list is shuffled. I do not know why. I suspect that this way, the blanks are better distributed. With the result that the game is harder.
  3. The list is passed to the method generateGame(int[][], List<Integer>) and the return value will be returned.
private int[][] generateGame(int[][] game) {
    List<Integer> positions = new ArrayList<Integer>();
    for (int i = 0; i < 81; i++)
        positions.add(i);
    Collections.shuffle(positions);
    return generateGame(game, positions);
}
  1. As long as there are positions in the list, the following will be executed:
    1. A position is taken from the list and stored in a variable.
    2. x and y are calculated from this position.
    3. Value at this position is stored in the variable temp.
    4. Value at this position is set to 0 (meaning the field is a blank).
    5. This step is critical. As the removal of the value at the position means that the game is no longer valid, the value at the position is put back. Otherwise the game stays the same.
  2. The game is returned.
private int[][] generateGame(int[][] game, List<Integer> positions) {
    while (positions.size() > 0) {
        int position = positions.remove(0);
        int x = position % 9;
        int y = position / 9;
        int temp = game[y][x];
        game[y][x] = 0;

        if (!isValid(game))
            game[y][x] = temp;
    }

    return game;
}

As you can see, this method is used to pass default values. So why the new int[] { 0 }? This is the most common way of passing an integer by reference, instead of by value.

private boolean isValid(int[][] game) {
    return isValid(game, 0, new int[] { 0 });
}

A valid game has in every row, every column, and every region the numbers 1 to 9. Additionally, there should only be one solution existing. To achieve this, all open fields are filled with the first valid value. Even after finding a solution, the search continues by putting the next valid value in an open field. If a second solution is found, then the search will be stopped and the method returns false. There will always be at least one solution (hence game is an incomplete solution), so if there are less than two solutions, the game is valid and the method returns true. Step by step:

  1. Check if a solution is found.
    • Found -> increase numberOfSolutions and return true if it equals 1; false otherwise.
    • Not found -> continue.
  2. Calculate x and y from index.
  3. Check if current field is a blank (equals 0).
    • True
      1. Fill a list with numbers 1 to 9.
      2. While the list contains numbers, execute the following:
        1. Get the next possible number. If the returned value equals -1, perform a break resulting a return of true.
        2. Set this number at current field.
        3. Call this method recursively and instantly check against the returned value.
          • True -> one or no solution found, continue search.
          • False -> more than one solution found, stop searching. Restore game and return false.
        4. Restore value of current field to 0 (meaning it is blank).
    • False
      1. Call this method recursively and instantly check against the returned value.
        • True -> continue (resulting in returning true).
        • False -> return false.
  4. Return true.
private boolean isValid(int[][] game, int index, int[] numberOfSolutions) {
    if (index > 80)
        return ++numberOfSolutions[0] == 1;

    int x = index % 9;
    int y = index / 9;

    if (game[y][x] == 0) {
        List<Integer> numbers = new ArrayList<Integer>();
        for (int i = 1; i <= 9; i++)
            numbers.add(i);

        while (numbers.size() > 0) {
            int number = getNextPossibleNumber(game, x, y, numbers);
            if (number == -1)
                break;
            game[y][x] = number;

            if (!isValid(game, index + 1, numberOfSolutions)) {
                game[y][x] = 0;
                return false;
            }
            game[y][x] = 0;
        }
    } else if (!isValid(game, index + 1, numberOfSolutions))
        return false;

    return true;
}

Check Game

With checking user input, we compare each field in the game against the corresponding field in the solution. The result is stored in a two dimensional boolean array. The selected number is also set to 0 (meaning no number is selected). All observers are notified that the model has changed with the corresponding UpdateAction.

public void checkGame() {
    selectedNumber = 0;
    for (int y = 0; y < 9; y++) {
        for (int x = 0; x < 9; x++)
            check[y][x] = game[y][x] == solution[y][x];
    }
    setChanged();
    notifyObservers(UpdateAction.CHECK);
}

View & Controller

There is not much to say about the view part, only the structure of the panels. The controllers react to user input and implement changes to the model. The model notifies it has been changed. The view responses to this notification and updates. These updates of the view are limited to changing colors and changing the number of a field. There is no rocket science involved.

Sudoku

The view also is the entry point of this application; the Sudoku class contains the main method. This class builds up the user interface by creating a JFrame and placing SudokuPanel and ButtonPanel inside this frame. It also creates the Game class, and adds SudokuPanel and ButtonPanel as observers to it.

SudokuPanel and Fields

SudokuPanel contains 9 sub panels each containing 9 fields. All sub panels and fields are placed in a GridLayout of 3x3. Each sub panel represents a region of a Sudoku game, and is primarily used for drawing a border visually separating each region. They also get the SudokuController added to them, which is in charge of processing user input by mouse.

ButtonPanel

ButtonPanel contains two panels with a titled border. The first panel contains three buttons. Button New for starting a new game, button Check for checking user input, and button Exit for exiting the application. The second panel contains 9 toggle buttons placed inside a button group. This way, they react like radio buttons. Clicking one of these toggle buttons will set the corresponding selected number in the Game class.

Points of Interest

  • For passing an integer by reference instead of by value, use an integer array.
  • isValid(game, 0, new int[] { 0 });
    private boolean isValid(int[][] game, int index, int[] numberOfSolutions);
  • Finding the base x and y values of a region, use the following:
  • int x1 = x < 3 ? 0 : x < 6 ? 3 : 6;
    int y1 = y < 3 ? 0 : y < 6 ? 3 : 6;

History

  • Initial release.

Apologies

One, if I have driven you mad with my bad English, I apologize. Two, if I have driven you mad before because usually my source doesn't contain comments, I apologize, and I can tell you, in this case, it does. Not because I acknowledge their usefulness, but to avoid discussions. Three, if I didn't mention before that you can use your right mouse button to clear a field, I apologize.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Eric Beijer
Software Developer Fortes Solutions BV
Netherlands Netherlands
It's good to be me.
Follow on   Twitter

Comments and Discussions

 
Questionunable to run the project. PinmemberRipunjoy Deka18-May-12 5:27 
AnswerRe: unable to run the project. PinmemberEric Beijer21-May-12 5:15 
QuestionDifficulty level PinmemberAprNgp24-Apr-12 20:50 
AnswerRe: Difficulty level [modified] PinmemberAprNgp24-Apr-12 21:04 
GeneralRe: Difficulty level PinmemberEric Beijer25-Apr-12 10:41 
Questiondownload trouble PinmemberMember 884778818-Apr-12 13:40 
AnswerRe: download trouble PinmemberEric Beijer20-Apr-12 4:54 
Questionhow to run the prog PinmemberMember 873227017-Mar-12 16:42 
pardon me for asking this question D: i am a beginner of java. i am using eclipse, i open all your java file and try to run it ask me for a builder and after i choose nth pop up :(
AnswerRe: how to run the prog PinmemberEric Beijer23-Mar-12 10:20 
Generalhow to run the prog Pinmemberfasc200017-Apr-12 17:56 
GeneralRe: how to run the prog PinmemberEric Beijer18-Apr-12 5:15 
Questionvery good coding ... we are using your code for our assignment Pinmembertidkerohit12-Jan-12 2:08 
AnswerRe: very good coding ... we are using your code for our assignment PinmemberEric Beijer12-Jan-12 2:17 
GeneralRe: very good coding ... we are using your code for our assignment Pinmembertidkerohit12-Jan-12 2:51 
GeneralGreat! PinmemberMarcelo Ricardo de Oliveira8-Jul-10 8:13 
GeneralRe: Great! PinmemberEric Beijer3-Aug-10 15:39 
GeneralI found a gem... PinmemberJörgen Andersson1-Jul-10 4:58 
GeneralRe: I found a gem... PinmemberEric Beijer1-Jul-10 5:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140415.2 | Last Updated 18 Apr 2012
Article Copyright 2010 by Eric Beijer
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid