You are on page 1of 7

GUI Sudoku Solver

Understand the Problem


This problem should be solved in five phases:

1. Read, display, and manipulate a sudoku in a SudokuGrid class


2. Create a Solver interface to allow for multiple solving algorithms
3. Solve a given SudokuGrid by incremental steps in a BruteForceSolver class that implements
Solver
4. Create a SudokuFrame class that extends JFrame to display a Solver for the user to watch the
solving algorithm
5. Create a Sudoku class that initializes everything and adds buttons so the user can watch the
solution worked out at three different speeds.

A Sudoku is a single-player puzzle where the player fills out a 9x9 grid (nine 3x3 subgrids) with the
numbers 1-9, with some values pre-stated. Each row, column and subgrid must contain exactly one
instance of each number 1, 2, , 9. Below is an example of a puzzle with its solution, and the kind of
input your game will take.

String[] sudokuString =
{
"53**7****",
"6**195***",
"*98****6*",
"8***6***3",
"4**8*3**1",
"7***2***6",
"*6****28*",
"***419**5",
"****8**79"
};

Phase 1: SudokuGrid - Model

Define a class, SudokuGrid that reads in a String array representation of a sudoku puzzle and converts it
to a 2D int array sudoku to hold the 9x9 grid of values. You should have one constructor that does this
and a default constructor that loads it with empty values. Its up to you to decide how to indicate an
empty cell.

DATA

Private int[][] sudoku;

METHODS

Default Constructor instantiates sudoku and stuffs it with empties


SudokuGrid(String[] str_data) parses str_data, instantiates sudoku, and loads it.
SudokuGrid(SudokuGrid sudokuOriginal) copy constructor that makes a deep copy of
sudokuOriginal.
Mutators for a cell of the grid and accessors for a cell and the entire grid itself. Dont forget to
make a copy of sudoku to return; dont return the actual array itself.
Private boolean tryValue(int row, int col, int value) checks if a given value is valid for the
given location (checks row, column, and subsquare, you may need helper methods).
displayToConsole() formats so that subsquares are clearly distinct
main() for testing purposes. Include output from this run.

//main for testing purposes


public static void main(String[] args)
{
String[] sudokuString =
{
"53**7****",
"6**195***",
"*98****6*",
"8***6***3",
"4**8*3**1",
"7***2***6",
"*6****28*",
"***419**5",
"****8**79"
};
SudokuGrid testSudoku = new SudokuGrid(sudokuString);
testSudoku.displayToConsole();

//test valid value


boolean testValid = testSudoku.tryValue(0, 8, 4);
System.out.println("Trying 4 at cell 0,8: " + testValid);
testSudoku.setPixel(0, 8, 4);
testSudoku.displayToConsole();

//test invalid value


testValid = testSudoku.tryValue(6, 3, 9);
System.out.println("Trying 9 at cell 6,3: " + testValid);
testSudoku.setPixel(6, 3, 9);
testSudoku.displayToConsole();
}

Phase 2: Solver Interface - Model

Define an interface, Solver, that contains these method signatures. Any class that implements Solver is
expected to store a given SudokuGrid and a copy of it. There will be two so that you will always be able
to identify which cells were initially empty (i.e. which cells to work on) and which had the initial values
(to leave alone). We define this interface to use in the GUI without being tied to a single type of solving
algorithm.
Public SudokuGrid getOriginal() returns a clean copy of the initial sudoku puzzle
Public SudokuGrid getSolution() returns a clean copy of the work-in-progress sudoku puzzle
Public boolean nextStep() will update the work-in-progress with one step. Will return true
until puzzle is completely solved.
Public boolean createSolution() will solve the puzzle from start to finish.

Phase 3: BruteForceSolver - Model


Next, we define a class BruteForceSolver that implements Solver. The brute force algorithm for solving
sudoku is fairly straightforward. Start at the first empty cell, increment the value inside until the value is
valid (in the row/column/subgrid). If its empty, put a 1 in. Go to the next empty cell and repeat. If a
cell has no valid values, empty the cell and go back to the previous cell and find its next valid value.
Repeat until the entire grid is complete. See the Backtracking section of the sudoku algorithm page at
https://en.wikipedia.org/wiki/Sudoku_solving_algorithms#Backtracking for more info.

DATA

Private SudokuGrid sudokuOriginal to store a copy of the original puzzle


Private SudokuGrid sudoku to store a work-in-progress puzzle
Private int[][] emptyPixels a [n][2] size array to store the row/col locations of the empty pixels
to easily find the next/previous empty cell of the puzzle
Private emptyTracker a counter to index back and forth through the emptyPixels array. Youll
use it like this: row = emptyPixels[emptyTracker][0] and col = emptyPixels[emptyTracker][1].

METHODS

Default constructor loads an grid of empties (tip: call default SudokuGrid constructor).
Initializes emptyTracker and calls findEmptyPixels()
BruteForceSolver(SudokuGrid sudokuInput) loads copies of sudokuInput into sudokuOriginal
and sudoku. Initializes emptyTracker and calls findEmptyPixels()
Define each of the methods listed above in the Solver interface section. Also note:
o nextStep() should be one change of one cell. It should take the current cell (based on
emptyTracker), find its next valid value and put it in or reset to empty. Based on that,
increment/decrement emptyTracker.
o createSolution() should loop through calls of nextStep() until the puzzle is solved.
Private void findEmptyPixels() initialize and load the emptyPixels array.
Main() for testing purposes. Include output from run.
//main for testing purposes
public static void main(String[] args)
{
String[] sudokuString =
{
"53**7****",
"6**195***",
"*98****6*",
"8***6***3",
"4**8*3**1",
"7***2***6",
"*6****28*",
"***419**5",
"****8**79"
};
String[] sudokuString2 =
{
"*3*7*****",
"2****9***",
"97***3***",
"******4**",
"**6***2**",
"**3******",
"***8***54",
"***3****9",
"*****2*6*"
};
String[] sudokuString3 =
{
"8*****137",
"*****8**2",
"7**2**4*6",
"5**182***",
"***6*3***",
"***459**1",
"6*1**5**3",
"9**3*****",
"234*****8"
};

//Solve sudoku 1
SudokuGrid testSudoku = new SudokuGrid(sudokuString);
testSudoku.displayToConsole();

BruteForceSolver solution = new BruteForceSolver(testSudoku);


solution.createSolution();
solution.displaySolution();

//Solve sudoku 2
testSudoku = new SudokuGrid(sudokuString2);
testSudoku.displayToConsole();
solution = new BruteForceSolver(testSudoku);
solution.createSolution();
solution.displaySolution();

//Solve sudoku 3
testSudoku = new SudokuGrid(sudokuString3);
testSudoku.displayToConsole();

solution = new BruteForceSolver(testSudoku);


solution.createSolution();
solution.displaySolution();
}

Phase 4: SudokuFrame View


This SudokuFrame class should extend the JFrame class. In it, you will use JPanels and JTextFields to
display the sudoku puzzle in a GUI. It holds the puzzle in the form of a Solver, so that it can untilize
multiple different solving algorithm classes. Dont forget the imports:

import javax.swing.*;
import java.awt.*;

DATA

Public Solver sudoku an instance of any class that implements the Solver interface
Public JPanel buttonPanel so the controller class can designate buttons itself
Private JPanel gridPanel the panel that displays the sudoku. I recommend using a 9x9
GridLayout for this panel. You may want to consider adding a 2D array of subpanels to more
easily graphically display the nine subsquares of a sudoku puzzle. If you do, use the 3x3
GridLayout for the subpanels and gridPanel. Display a border on a panel like this:
panel.setBorder(BorderFactory.createLineBorder(Color.black))
Private JTextField[][] gridTextFields the array of JTextFields to display the sudoku values.

METHODS

Constructor SudokuFrame(String title, Solver sudoku) saves sudoku, all the panels, and places
the gridTextFields into the appropriate panel(s). Set the layout as BorderLayout so that the
gridPanel can be added to BorderLayout.CENTER and the buttonPanel at BorderLayout.SOUTH.
Will call paint() to load values into gridTextFields
Public void paint() update gridTextFields with values from sudoku and display to the user
using validate() and setVisible(true).
You may want to utilize other optional methods like createTextFields(), createSubGrids(), etc.

Use logic when creating the textFields to visually differentiate between the initial preset values and
those that are being filled in. This is where knowing the original puzzle values comes in handy.
For instance, you could set the original values bold and black and the filled in values smaller and
blue. Dont forget to center all of the textFields.

Phase 5: Sudoku Controller


This class instantiates the SudokuFrame class and adds to it three buttons that allow the user to watch
the sudoku being solved at a speed either fast, medium, or slow, and change speed during the solving.
The user will watch the numbers flicker in and out of cells like the gif on the wikipedia page listed
previously. This class contains the GUI puzzle run, so make sure it gets its own java file. It will include
two inner classes: SolveThread, which extends Thread, and TimeListener, which implements
ActionListener. Dont forget the imports:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

DATA

private final int SLOW_TIME = 500 milliseconds to pause between steps


private final int MEDIUM_TIME = 100 milliseconds to pause between steps
private final int FAST_TIME = 1 milliseconds to pause between steps
private static SudokuFrame mySudokuFrame
private static JButton slowButton, mediumButton, fastButton
private int delayTime = SLOW_TIME current milliseconds to pause between steps

METHODS

Constructor Sudoku(Solver solver) this will accept any class that implements the Solver
interface. (Were only using BruteForceSolver here, but you could write a HeuristicSolver class
that implemented Solver and use it here just the same). Use that Solver to instantiate
mySudokuFrame, size it, and set its default close operation. Instantiate, set up, and add the
buttons to mySudokuFrame.buttonPanel. Create a SolveThread to start solving.
main() this is the main that the puzzle GUI will run from. Use a sudokuString array from a
previous phases main() to create a SudokuGrid and then a BruteForceSolver. Then create and
instantiate Sudoku to start running the GUI. Do not display anything to the console.
Boolean setDelayTime(char newTime) sets delayTime based on given input. I used char
newTime with s for slow, m for medium, and f for fast to determine what to set delayTime
to. Feel free to use alternate input. This is what the TimeListener will call with input depending
on which button was pressed.

INNER CLASSES

SolveThread extends Thread contains 2 methods:


o Public void run() overrides run() of Thread. This will solve the sudoku in the same way
createSolution() did in BruteForceSolver except that it will pause by calling
doNothing(int milliseconds) and update the display by calling paint() on
mySudokuFrame.
o Private void doNothing(int milliseconds) will call Threads sleep for milliseconds.
Dont forget to surround in a try/catch block with InterruptedException.
TimeListener implements ActionListener contains 1 method:
o Public void actionPerformed(ActionEvent e) this will read the actionCommand from
the pressed button and send that parsed input into setDelayTime().

All Done! Additional features could be adding the previously proposed HeuristicSolver class, allowing
the user to input a sudoku to solve, and adding buttons to start/stop the solving process.

Heres a stillshot of what my program run looks like mid-run without the additional features:

You might also like