I do like playing Sudoku. I have a nice handy little android program on my phone and when I have a few minutes I like to solve a puzzle or two.

Not content with that, I thought I would develop a java version. I came across the book Programming Sudoku (Technology in Action) by Wei-Meng Lee.

In the book, Lee develops an application in Visual Basic, and I am not ashamed to say that I have taken much of his code and ideas and converted it to Java. Any way if anyone is interested here is the code:

 

/*******************************************************************************
 * Copyright (c) 2013 Robert Kovacs.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     Robert Kovacs - initial API and implementation
 ******************************************************************************/
 
package org.nofrills.games.sudoku;
 
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Stack;
 
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileFilter;
 
public class SudokuApplet extends JApplet implements ActionListener, MouseListener {
 
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    protected boolean gameStarted = false;;
    protected boolean gameComplete = false;
 
    public Integer[][] board = new Integer[10][10];
    public String[][] possible = new String[10][10];
    public boolean[][] initial = new boolean[10][10];
 
    public Stack<Integer[][]> actualStack = new Stack<Integer[][]>();
    public Stack<String[][]> possibleStack = new Stack<String[][]>();
    public boolean bruteForceStop = false;
 
    private Stack<SudokuMove> undoMoves = new Stack<SudokuMove>();
    private Stack<SudokuMove> redoMoves = new Stack<SudokuMove>();
 
    JFileChooser jfc = new JFileChooser();
    public int currentNumber;
    private JTextArea textArea;
    private long startTime;
    private Thread timerThread;
    protected boolean okay;
    private JLabel jlElapsed;
    private JButton jb1;
    private JButton jb2;
    private JButton jb3;
    private JButton jb4;
    private JButton jb5;
    private JButton jb6;
    private JButton jb7;
    private JButton jb8;
    private JButton jb9;
    private File game;
 
    /**
     * Create the applet.
     */
    public SudokuApplet() {
 
        createMenubar();
        createToolbar();
 
        JPanel panel_1 = new JPanel();
        getContentPane().add(panel_1, BorderLayout.CENTER);
        panel_1.setLayout(new BorderLayout(0, 0));
 
        JPanel jpGrid = new JPanel();
        panel_1.add(jpGrid, BorderLayout.CENTER);
        jpGrid.setLayout(new GridLayout(3, 3, 0, 0));
 
        createPanel(jpGrid, 1, 1);
        createPanel(jpGrid, 2, 1);
        createPanel(jpGrid, 3, 1);
 
        createPanel(jpGrid, 1, 2);
        createPanel(jpGrid, 2, 2);
        createPanel(jpGrid, 3, 2);
 
        createPanel(jpGrid, 1, 3);
        createPanel(jpGrid, 2, 3);
        createPanel(jpGrid, 3, 3);
 
        JPanel jpButtons = new JPanel();
        jpButtons.setPreferredSize(new Dimension(10, 35));
        panel_1.add(jpButtons, BorderLayout.SOUTH);
 
        JButton btnHint = new JButton("Hint");
        btnHint.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doSolveGame(false);
            }
        });
        jpButtons.add(btnHint);
 
        JButton btnSolve = new JButton("Solve");
        btnSolve.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doSolveGame(true);
            }
        });
        jpButtons.add(btnSolve);
 
        JPanel jpStatus = new JPanel();
        jpStatus.setPreferredSize(new Dimension(10, 25));
        getContentPane().add(jpStatus, BorderLayout.SOUTH);
 
        jlElapsed = new JLabel("");
        jlElapsed.setHorizontalAlignment(SwingConstants.LEFT);
        jpStatus.add(jlElapsed);
 
        textArea = new JTextArea();
        textArea.setBackground(UIManager.getColor("Button.background"));
        textArea.setEditable(false);
        textArea.setColumns(20);
 
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setViewportBorder(new TitledBorder(null, "Activities", TitledBorder.LEADING, TitledBorder.TOP, null, null));
        getContentPane().add(scrollPane, BorderLayout.EAST);
        scrollPane.setViewportView(textArea);
 
    }
 
    protected void doSolveGame(final boolean solve) {
 
        Runnable r = new Runnable() {
 
            @Override
            public void run() {
                int mCount = 0;
                do {
                    mCount = 0;
                    int count = 0;
                    do {
                        count = doCrme(true);
                        mCount += count;
                    } while (count > 0);
 
                    // ************************
                    // Lone Rangers
                    // ************************
                    do {
                        count = doLoneRangerMiniGrid(true);
                        mCount += count;
                    } while (count > 0);
 
                    do {
                        count = doLoneRangerRow(true);
                        mCount += count;
                    } while (count > 0);
 
                    do {
                        count = doLoneRangerColumn(true);
                        mCount += count;
                    } while (count > 0);
 
                    // ************************
                    // Twins
                    // ************************
                    do {
                        count = doTwinsMiniGrid(true);
                        mCount += count;
                    } while (count > 0);
 
                    do {
                        count = doTwinsRow(true);
                        mCount += count;
                    } while (count > 0);
 
                    do {
                        count = doTwinsColumn(true);
                        mCount += count;
                    } while (count > 0);
 
                    // ************************
                    // Triplets
                    // ************************
                    do {
                        count = doTripletsMiniGrid(true);
                        mCount += count;
                    } while (count > 0);
 
                    do {
                        count = doTripletsRow(true);
                        mCount += count;
                    } while (count > 0);
 
                    do {
                        count = doTripletsColumn(true);
                        mCount += count;
                    } while (count > 0);
 
                    // ************************
                    // Brute Force
                    // ************************
                    do {
                        bruteForceStop = false;
                        count = doBruteForce(true);
                        mCount += count;
                    } while (count > 0);
 
                } while (!isGameComplete() && mCount > 0);
            }
        };
        if (solve) {
            new Thread(r).start();
        } else {
            int count = doCrme(false);
            if (count > 0) {
                return;
            }
            count = doLoneRangerMiniGrid(false);
            if (count > 0) {
                return;
            }
            count = doLoneRangerRow(false);
            if (count > 0) {
                return;
            }
            count = doLoneRangerColumn(false);
            if (count > 0) {
                return;
            }
            count = doTwinsMiniGrid(false);
            if (count > 0) {
                return;
            }
            count = doTwinsRow(false);
            if (count > 0) {
                return;
            }
            count = doTwinsColumn(false);
            if (count > 0) {
                return;
            }
            count = doTripletsMiniGrid(false);
            if (count > 0) {
                return;
            }
            count = doTripletsRow(false);
            if (count > 0) {
                return;
            }
            count = doTripletsColumn(false);
            if (count > 0) {
                return;
            }
            bruteForceStop = false;
            count = doBruteForce(false);
            if (count > 0) {
                return;
            }
        }
    }
 
    private int doBruteForce(boolean solve) {
 
        Point p = findCellWithFewestValues();
        int c = p.x;
        int r = p.y;
 
        String possValues = possible[c][r];
 
        Integer[][] bc = board.clone();
        actualStack.push(bc);
        possibleStack.push(possible.clone());
        try {
            if (possValues.length() == 0) {
                throw new Exception();
            }
            for (int i = 0; i < possValues.length(); i++) {
                setValue(c, r, Integer.parseInt(possValues.substring(i, i + 1)));
                if (isGameComplete()) {
                    bruteForceStop = true;
                    return 0;
                } else {
                    doBruteForce(solve);
                    if (bruteForceStop) {
                        return 0;
                    }
                }
            }
        } catch (Exception e) {
            textArea.append("Invalid move ... backtracking");
            board = actualStack.pop();
            possible = possibleStack.pop();
        }
        return 0;
    }
 
    private Point findCellWithFewestValues() {
        Point p = new Point();
        int min = 10;
        for (int c = 1; c <= 9; c++) {
            for (int r = 1; r <= 9; r++) {
                if (board[c][r] == 0 && possible[c][r].length() < min) {
                    min = possible[c][r].length();
                    p.x = c;
                    p.y = r;
                }
            }
        }
        return p;
    }
 
    private int doTripletsMiniGrid(boolean solve) {
        boolean[][] tripletsFound = new boolean[10][10];
        int count = 0;
        for (int numberToCheck = 1; numberToCheck <= 9; numberToCheck++) {
            for (int c = 1; c <= 9; c++) {
                for (int r = 1; r <= 9; r++) {
                    if (board[c][r] == 0 & getPossibleValues(c, r).size() == 3) {
 
                        String tripletsLocation = "" + c + r;
                        int trip1c = c;
                        int trip1r = r;
                        int trip2c = 0;
                        int trip2r = 0;
                        int trip3c = 0;
                        int trip3r = 0;
                        int sc = c - ((c - 1) % 3);
                        int sr = r - ((r - 1) % 3);
 
                        for (int rr = sr; rr <= sr + 2; rr++) {
                            for (int cc = sc; cc <= sc + 2; cc++) {
                                if (cc == c & rr == r) {
                                    continue;
                                }
                                if ((possible[cc][rr].equals(possible[c][r]))
                                        || (possible[cc][rr].length() == 2 && possible[c][r].contains(possible[cc][rr].substring(0, 1)) && possible[c][r].contains(possible[cc][rr].substring(1, 2)))) {
                                    tripletsLocation += ("" + cc + rr);
                                    if (trip2c == 0) {
                                        trip2c = cc;
                                        trip2r = rr;
                                    } else {
                                        trip3c = cc;
                                        trip3r = rr;
                                    }
                                }
                            }
                        }
 
                        if (tripletsLocation.length() == 6) {
                            if (tripletsFound[c][r]) {
                                continue;
                            }
                            tripletsFound[trip1c][trip1r] = true;
                            tripletsFound[trip2c][trip2r] = true;
                            tripletsFound[trip3c][trip3r] = true;
                            textArea.append("Triplets found in minigrid at " + tripletsLocation + "\n");
 
                            for (int rrr = sr; rrr <= sr + 2; rrr++) {
                                for (int ccc = sc; ccc <= sc + 2; ccc++) {
                                    if (board[ccc][rrr] == 0 & ccc != trip1c & ccc != trip2c & ccc != trip3c & rrr != trip1r & rrr != trip2r & rrr != trip3r) {
                                        String origPossible = possible[ccc][rrr];
                                        String newPoss = possible[ccc][rrr];
                                        newPoss.replace(possible[trip1c][trip1r], "");
                                        newPoss.replace(possible[trip2c][trip2r], "");
                                        newPoss.replace(possible[trip3c][trip3r], "");
 
                                        if (!origPossible.equals(possible[ccc][rrr])) {
                                            if (possible[ccc][rrr].length() == 0) {
                                                throw new RuntimeException("Invalid move somewhere else.");
                                            }
                                            if (possible[ccc][rrr].length() == 1) {
                                                setValue(ccc, rrr, numberToCheck);
                                            }
                                            count++;
                                            if (!solve) {
                                                return count;
                                            }
                                        }
 
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return count;
    }
 
    private int doTripletsRow(boolean solve) {
        boolean[][] tripletsFound = new boolean[10][10];
        int count = 0;
        for (int numberToCheck = 1; numberToCheck <= 9; numberToCheck++) {
            for (int c = 1; c <= 9; c++) {
                for (int r = 1; r <= 9; r++) {
                    if (board[c][r] == 0 & getPossibleValues(c, r).size() == 3) {
 
                        String tripletsLocation = "" + c + r;
                        int trip1c = c;
                        int trip1r = r;
                        int trip2c = 0;
                        int trip2r = 0;
                        int trip3c = 0;
                        int trip3r = 0;
                        int sr = r;
 
                        for (int cc = 1; cc <= 9; cc++) {
                            if (cc == c & sr == r) {
                                continue;
                            }
                            if ((possible[cc][sr].equals(possible[c][r]))
                                    || (possible[cc][sr].length() == 2 && possible[c][r].contains(possible[cc][sr].substring(0, 1)) && possible[c][r].contains(possible[cc][sr].substring(1, 2)))) {
                                tripletsLocation += ("" + cc + sr);
                                if (trip2c == 0) {
                                    trip2c = cc;
                                    trip2r = sr;
                                } else {
                                    trip3c = cc;
                                    trip3r = sr;
                                }
                            }
                        }
 
                        if (tripletsLocation.length() == 6) {
                            if (tripletsFound[c][r]) {
                                continue;
                            }
                            tripletsFound[trip1c][trip1r] = true;
                            tripletsFound[trip2c][trip2r] = true;
                            tripletsFound[trip3c][trip3r] = true;
                            textArea.append("Triplets found in row at " + tripletsLocation + "\n");
 
                            for (int ccc = 1; ccc <= 9; ccc++) {
                                if (board[ccc][sr] == 0 & ccc != trip1c & ccc != trip2c & ccc != trip3c & sr != trip1r & sr != trip2r & sr != trip3r) {
                                    String origPossible = possible[ccc][sr];
                                    String newPoss = possible[ccc][sr];
                                    newPoss.replace(possible[trip1c][trip1r], "");
                                    newPoss.replace(possible[trip2c][trip2r], "");
                                    newPoss.replace(possible[trip3c][trip3r], "");
 
                                    if (!origPossible.equals(possible[ccc][sr])) {
                                        if (possible[ccc][sr].length() == 0) {
                                            throw new RuntimeException("Invalid move somewhere else.");
                                        }
                                        if (possible[ccc][sr].length() == 1) {
                                            setValue(ccc, sr, numberToCheck);
                                        }
                                        count++;
                                        if (!solve) {
                                            return count;
                                        }
                                    }
 
                                }
                            }
                        }
                    }
                }
            }
        }
        return count;
    }
 
    private int doTripletsColumn(boolean solve) {
        boolean[][] tripletsFound = new boolean[10][10];
        int count = 0;
        for (int numberToCheck = 1; numberToCheck <= 9; numberToCheck++) {
            for (int c = 1; c <= 9; c++) {
                for (int r = 1; r <= 9; r++) {
                    if (board[c][r] == 0 & getPossibleValues(c, r).size() == 3) {
 
                        String tripletsLocation = "" + c + r;
                        int trip1c = c;
                        int trip1r = r;
                        int trip2c = 0;
                        int trip2r = 0;
                        int trip3c = 0;
                        int trip3r = 0;
                        int sc = c;
 
                        for (int rr = 1; rr <= 9; rr++) {
                            if (sc == c & rr == r) {
                                continue;
                            }
                            if ((possible[sc][rr].equals(possible[c][r]))
                                    || (possible[sc][rr].length() == 2 && possible[c][r].contains(possible[sc][rr].substring(0, 1)) && possible[c][r].contains(possible[sc][rr].substring(1, 2)))) {
                                tripletsLocation += ("" + sc + rr);
                                if (trip2c == 0) {
                                    trip2c = sc;
                                    trip2r = rr;
                                } else {
                                    trip3c = sc;
                                    trip3r = rr;
                                }
                            }
                        }
 
                        if (tripletsLocation.length() == 6) {
                            if (tripletsFound[c][r]) {
                                continue;
                            }
                            tripletsFound[trip1c][trip1r] = true;
                            tripletsFound[trip2c][trip2r] = true;
                            tripletsFound[trip3c][trip3r] = true;
                            textArea.append("Triplets found in column at " + tripletsLocation + "\n");
 
                            for (int rrr = 1; rrr <= 9; rrr++) {
                                if (board[sc][rrr] == 0 & sc != trip1c & sc != trip2c & sc != trip3c & rrr != trip1r & rrr != trip2r & rrr != trip3r) {
                                    String origPossible = possible[sc][rrr];
                                    String newPoss = possible[sc][rrr];
                                    newPoss.replace(possible[trip1c][trip1r], "");
                                    newPoss.replace(possible[trip2c][trip2r], "");
                                    newPoss.replace(possible[trip3c][trip3r], "");
 
                                    if (!origPossible.equals(possible[sc][rrr])) {
                                        if (possible[sc][rrr].length() == 0) {
                                            throw new RuntimeException("Invalid move somewhere else.");
                                        }
                                        if (possible[sc][rrr].length() == 1) {
                                            setValue(sc, rrr, numberToCheck);
                                        }
                                        count++;
                                        if (!solve) {
                                            return count;
                                        }
                                    }
 
                                }
                            }
                        }
                    }
                }
            }
        }
        return count;
    }
 
    protected int doTwinsColumn(boolean solve) {
        boolean[][] twinsFound = new boolean[10][10];
        int count = 0;
        for (int numberToCheck = 1; numberToCheck <= 9; numberToCheck++) {
            for (int c = 1; c <= 9; c++) {
                for (int r = 1; r <= 9; r++) {
                    if (board[c][r] == 0 & getPossibleValues(c, r).size() == 2) {
                        int sc = c;
 
                        for (int rr = 1; rr <= 9; rr++) {
                            if (!(rr == r)) {
                                if (possible[sc][rr].equals(possible[c][r])) {
                                    if (twinsFound[c][r]) {
                                        continue;
                                    }
                                    twinsFound[c][r] = true;
                                    twinsFound[sc][rr] = true;
                                    textArea.append("Twins found in column at:" + c + "," + r + " and " + sc + "," + rr + "\n");
 
                                    for (int rrr = 1; rrr <= 9; rrr++) {
                                        if (board[sc][rrr] == 0 & rrr != r & rrr != rr) {
                                            String origPossible = possible[sc][rrr];
                                            String newPossible = possible[sc][rrr];
                                            String twins = possible[c][r];
                                            newPossible = newPossible.replace(twins.substring(0, 1), "");
                                            newPossible = newPossible.replace(twins.substring(1, 2), "");
                                            if (!origPossible.equals(newPossible)) {
                                                possible[sc][rrr] = newPossible;
                                                if (possible[sc][rr].length() == 0) {
                                                    throw new RuntimeException("Invalid move somewhere else.");
                                                }
                                                if (possible[sc][rrr].length() == 1) {
                                                    setValue(sc, rrr, numberToCheck);
                                                }
                                                count++;
                                            }
                                        }
                                    }
                                    if (!solve) {
                                        return count;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return count;
    }
 
    protected int doTwinsRow(boolean solve) {
        boolean[][] twinsFound = new boolean[10][10];
        int count = 0;
        for (int numberToCheck = 1; numberToCheck <= 9; numberToCheck++) {
            for (int c = 1; c <= 9; c++) {
                for (int r = 1; r <= 9; r++) {
                    if (board[c][r] == 0 & getPossibleValues(c, r).size() == 2) {
                        int sr = r;
 
                        for (int cc = 1; cc <= 9; cc++) {
                            if (!(cc == c)) {
                                if (possible[cc][sr].equals(possible[c][r])) {
                                    if (twinsFound[c][r]) {
                                        continue;
                                    }
                                    twinsFound[c][r] = true;
                                    twinsFound[cc][sr] = true;
                                    textArea.append("Twins found in row at:" + c + "," + r + " and " + cc + "," + sr + "\n");
 
                                    for (int ccc = 1; ccc <= 9; ccc++) {
                                        if (board[ccc][sr] == 0 & ccc != c & ccc != cc) {
                                            String origPossible = possible[ccc][sr];
                                            String newPossible = possible[ccc][sr];
                                            String twins = possible[c][r];
                                            newPossible = newPossible.replace(twins.substring(0, 1), "");
                                            newPossible = newPossible.replace(twins.substring(1, 2), "");
                                            if (!origPossible.equals(newPossible)) {
                                                possible[ccc][sr] = newPossible;
                                                if (possible[ccc][sr].length() == 0) {
                                                    throw new RuntimeException("Invalid move somewhere else.");
                                                }
                                                if (possible[ccc][sr].length() == 1) {
                                                    setValue(ccc, sr, numberToCheck);
                                                }
                                                count++;
                                            }
                                        }
                                    }
                                    if (!solve) {
                                        return count;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return count;
    }
 
    protected int doTwinsMiniGrid(boolean solve) {
        boolean[][] twinsFound = new boolean[10][10];
        int count = 0;
        for (int numberToCheck = 1; numberToCheck <= 9; numberToCheck++) {
            for (int c = 1; c <= 9; c++) {
                for (int r = 1; r <= 9; r++) {
                    if (board[c][r] == 0 & getPossibleValues(c, r).size() == 2) {
                        int sc = c - ((c - 1) % 3);
                        int sr = r - ((r - 1) % 3);
 
                        for (int rr = sr; rr <= sr + 2; rr++) {
                            for (int cc = sc; cc <= sc + 2; cc++) {
                                if (!(cc == c) & (rr == r)) {
                                    if (possible[cc][rr].equals(possible[c][r])) {
                                        if (twinsFound[c][r]) {
                                            continue;
                                        }
                                        twinsFound[c][r] = true;
                                        twinsFound[cc][rr] = true;
                                        textArea.append("Twins found in minigrid at:" + c + "," + r + " and " + cc + "," + rr + "\n");
                                        for (int rrr = sr; rrr <= sr + 2; rrr++) {
                                            for (int ccc = sc; ccc <= sc + 2; ccc++) {
                                                if (board[ccc][rrr] == 0 & !possible[ccc][rrr].equals(possible[c][r])) {
                                                    String origPossible = possible[ccc][rrr];
                                                    possible[ccc][rrr].replace(possible[c][r].substring(0, 1), "");
                                                    possible[ccc][rrr].replace(possible[c][r].substring(1, 2), "");
                                                    if (!origPossible.equals(possible[ccc][rrr])) {
                                                        if (possible[ccc][rrr].length() == 0) {
                                                            throw new RuntimeException("Invalid move somewhere else.");
                                                        }
                                                        if (possible[ccc][rrr].length() == 1) {
                                                            setValue(ccc, rrr, numberToCheck);
                                                        }
                                                        count++;
                                                        if (!solve) {
                                                            return count;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return count;
    }
 
    private int doLoneRangerRow(boolean solve) {
        int count = 0;
        for (int y = 1; y <= 9; y++) {
            for (int numberToCheck = 1; numberToCheck <= 9; numberToCheck++) {
                int occurencies = 0;
                int xP = 0;
                int yP = 0;
                for (int x = 1; x <= 9; x++) {
                    if (board[x][y] == 0 & getPossibleValues(x, y).contains(numberToCheck)) {
                        occurencies++;
                        xP = x;
                        yP = y;
                    }
                    if (occurencies > 1) {
                        break;
                    }
                }
                if (occurencies == 1) {
                    textArea.append("Using LoneRanger Row method\n");
                    setValue(xP, yP, numberToCheck);
                    count++;
                    if (!solve) {
                        return count;
                    }
                }
            }
        }
        return count;
    }
 
    private int doLoneRangerMiniGrid(boolean solve) {
        int count = 0;
        for (int numberToCheck = 1; numberToCheck <= 9; numberToCheck++) {
            for (int x = 1; x <= 9; x += 3) {
                for (int y = 1; y <= 9; y += 3) {
                    int occurencies = 0;
                    int xP = 0;
                    int yP = 0;
                    for (int xx = 0; xx <= 2; xx++) {
                        for (int yy = 0; yy <= 2; yy++) {
                            if (board[x + xx][y + yy] == 0 & getPossibleValues(x + xx, y + yy).contains(numberToCheck)) {
                                occurencies++;
                                xP = x + xx;
                                yP = y + yy;
                            }
                            if (occurencies > 1) {
                                break;
                            }
 
                        }
                    }
                    if (occurencies == 1) {
                        textArea.append("Using LoneRanger Mini grid method\n");
                        setValue(xP, yP, numberToCheck);
                        count++;
                        if (!solve) {
                            return count;
                        }
                    }
 
                }
            }
        }
        return count;
    }
 
    private int doLoneRangerColumn(boolean solve) {
        int count = 0;
        for (int x = 1; x <= 9; x++) {
            for (int numberToCheck = 1; numberToCheck <= 9; numberToCheck++) {
                int occurencies = 0;
                int xP = 0;
                int yP = 0;
                for (int y = 1; y <= 9; y++) {
                    if (board[x][y] == 0 & getPossibleValues(x, y).contains(numberToCheck)) {
                        occurencies++;
                        xP = x;
                        yP = y;
                    }
                    if (occurencies > 1) {
                        break;
                    }
                }
                if (occurencies == 1) {
                    textArea.append("Using LoneRanger Column method\n");
                    setValue(xP, yP, numberToCheck);
                    count++;
                    if (!solve) {
                        return count;
                    }
                }
            }
        }
        return count;
    }
 
    /**
     * Performs the CRME - Column Row Minigrid Elimination
     * 
     * Basically if only 1 value is possible for a cell then set the cell with
     * it
     * 
     * @param solve
     */
    protected int doCrme(boolean solve) {
        int count = 0;
        for (int y = 1; y <= 9; y++) {
            for (int x = 1; x <= 9; x++) {
                if (board[x][y] == 0) {
                    List<Integer> values = getPossibleValues(x, y);
                    if (values.size() == 1) {
                        textArea.append("Using CRME method\n");
                        setValue(x, y, values.get(0));
                        count++;
                        if (!solve) {
                            return count;
                        }
                    }
                }
            }
        }
        return count;
    }
 
    protected List<Integer> getPossibleValues(int x, int y) {
        ArrayList<Integer> values = new ArrayList<Integer>();
        for (int i = 1; i <= 9; i++) {
            if (isValidMove(x, y, i)) {
                values.add(i);
            }
        }
        return values;
    }
 
    private void createMenubar() {
        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);
 
        JMenu mnFile = new JMenu("File");
        menuBar.add(mnFile);
 
        JMenuItem mntmNew = new JMenuItem("New");
        mntmNew.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                doNewGame();
            }
        });
        mnFile.add(mntmNew);
 
        JMenuItem mntmOpen = new JMenuItem("Open");
        mntmOpen.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doOpenGame();
            }
        });
        mnFile.add(mntmOpen);
 
        JMenuItem mntmSave = new JMenuItem("Save");
        mntmSave.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (game == null) {
                    doSaveGameAs();
                } else {
                    doSaveGame();
                }
            }
        });
        mnFile.add(mntmSave);
 
        JMenuItem mntmSaveAs = new JMenuItem("Save As ...");
        mntmSaveAs.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doSaveGameAs();
            }
        });
        mnFile.add(mntmSaveAs);
 
        JMenuItem mntmExit = new JMenuItem("Exit");
        mntmExit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (gameStarted) {
                    int result = JOptionPane.showConfirmDialog(null, "Do you want to save the game", "Question", JOptionPane.YES_NO_CANCEL_OPTION);
                    if (result == JOptionPane.CANCEL_OPTION) {
                        return;
                    }
                    if (result == JOptionPane.YES_OPTION) {
                        if (game == null) {
                            doSaveGameAs();
                        } else {
                            doSaveGame();
                        }
                    }
                }
                System.exit(1);
            }
        });
        mnFile.add(mntmExit);
 
        JMenu mnEdit = new JMenu("Edit");
        menuBar.add(mnEdit);
 
        JMenuItem mntmUndo = new JMenuItem("Undo");
        mntmUndo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doUndo();
            }
        });
        mnEdit.add(mntmUndo);
 
        JMenuItem mntmRedo = new JMenuItem("Redo");
        mntmRedo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                doRedo();
            }
        });
        mnEdit.add(mntmRedo);
 
        JMenu mnLevel = new JMenu("Level");
        menuBar.add(mnLevel);
 
        JMenuItem mntmEasy = new JMenuItem("Easy");
        mnLevel.add(mntmEasy);
 
        JMenuItem mntmMedium = new JMenuItem("Medium");
        mnLevel.add(mntmMedium);
 
        JMenuItem mntmDificult = new JMenuItem("Difficult");
        mnLevel.add(mntmDificult);
 
        JMenuItem mntmExtremelyDifficult = new JMenuItem("Extremely difficult");
        mnLevel.add(mntmExtremelyDifficult);
 
        JMenu mnHelp = new JMenu("Help");
        menuBar.add(mnHelp);
 
        JMenuItem mntmAbout = new JMenuItem("About ...");
        mnHelp.add(mntmAbout);
    }
 
    protected void doRedo() {
        if (redoMoves.isEmpty()) {
            return;
        }
        SudokuMove move = redoMoves.pop();
        board[move.x][move.y] = move.newValue;
        textArea.append("Redo [" + move.x + "," + move.y + "] to " + move.newValue + "\n");
        undoMoves.push(move);
        updateButtons();
        updatePossibleValues();
    }
 
    protected void doUndo() {
        if (undoMoves.isEmpty()) {
            return;
        }
        SudokuMove move = undoMoves.pop();
        board[move.x][move.y] = move.oldValue;
        textArea.append("Undo [" + move.x + "," + move.y + "] to " + move.oldValue + "\n");
        redoMoves.push(move);
        updateButtons();
        updatePossibleValues();
    }
 
    protected void doSaveGame() {
        if (!gameStarted) {
            return;
        }
        try {
            FileOutputStream os = new FileOutputStream(game);
            if (!game.exists()) {
                game.createNewFile();
            }
            StringBuffer sb = new StringBuffer();
            for (int y = 1; y <= 9; y++) {
                for (int x = 1; x <= 9; x++) {
                    sb.append("" + board[x][y]);
                }
            }
            os.write(sb.toString().getBytes());
            os.flush();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        JOptionPane.showMessageDialog(this, "Game saved");
    }
 
    protected void doSaveGameAs() {
        if (!gameStarted) {
            return;
        }
        int result = jfc.showSaveDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            game = jfc.getSelectedFile();
            doSaveGame();
        }
 
    }
 
    protected void doOpenGame() {
        jfc.setFileFilter(new SudokuFileFilter());
        int result = jfc.showOpenDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            game = jfc.getSelectedFile();
            try {
                BufferedReader br = new BufferedReader(new FileReader(game));
                String s = br.readLine();
                for (int y = 1; y <= 9; y++) {
                    for (int x = 1; x <= 9; x++) {
                        int pos = (x - 1) + ((y - 1) * 9);
                        String n = s.substring(pos, pos + 1);
                        int val = Integer.parseInt(n);
                        board[x][y] = val;
                        if (val == 0) {
                            initial[x][y] = false;
                        } else {
                            initial[x][y] = true;
                        }
                    }
                }
                br.close();
                gameStarted = true;
                gameComplete = false;
                updatePossibleValues();
                repaint();
                textArea.setText("");
                textArea.append("Game opened: " + game.getName() + "\n");
 
                startTimer();
 
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    private void startTimer() {
        startTime = Calendar.getInstance().getTimeInMillis();
        okay = true;
        Runnable r = new Runnable() {
 
            @Override
            public void run() {
                while (okay) {
                    long currentTime = Calendar.getInstance().getTimeInMillis();
                    long elapsed = (currentTime - startTime);
                    Calendar c = Calendar.getInstance();
                    c.setTimeInMillis(elapsed);
 
                    long secs = c.get(Calendar.SECOND);
                    long mins = c.get(Calendar.MINUTE);
 
                    if (gameStarted & !gameComplete) {
                        jlElapsed.setText("Elapsed time " + mins + ":" + secs + "");
                    }
 
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Stopping");
            }
        };
 
        timerThread = new Thread(r);
        timerThread.start();
    }
 
    protected void doNewGame() {
        game = null;
        textArea.setText("");
        for (int y = 1; y <= 9; y++) {
            for (int x = 1; x <= 9; x++) {
                board[x][y] = 0;
                initial[x][y] = false;
            }
        }
        updateButtons();
        updatePossibleValues();
        gameStarted = true;
        gameComplete = false;
        repaint();
        startTimer();
    }
 
    private void createToolbar() {
        JToolBar toolBar = new JToolBar();
        toolBar.setFloatable(false);
        getContentPane().add(toolBar, BorderLayout.NORTH);
 
        JLabel lblSelectNumber = new JLabel("Select number");
        toolBar.add(lblSelectNumber);
 
        jb1 = new JButton("1");
        toolBar.add(jb1);
 
        jb2 = new JButton("2");
        toolBar.add(jb2);
 
        jb3 = new JButton("3");
        toolBar.add(jb3);
 
        jb4 = new JButton("4");
        toolBar.add(jb4);
 
        jb5 = new JButton("5");
        toolBar.add(jb5);
 
        jb6 = new JButton("6");
        toolBar.add(jb6);
 
        jb7 = new JButton("7");
        toolBar.add(jb7);
 
        jb8 = new JButton("8");
        toolBar.add(jb8);
 
        jb9 = new JButton("9");
        toolBar.add(jb9);
 
        jb1.addActionListener(this);
        jb2.addActionListener(this);
        jb3.addActionListener(this);
        jb4.addActionListener(this);
        jb5.addActionListener(this);
        jb6.addActionListener(this);
        jb7.addActionListener(this);
        jb8.addActionListener(this);
        jb9.addActionListener(this);
 
        JButton jbClear = new JButton("Clear");
        jbClear.addActionListener(this);
        toolBar.add(jbClear);
    }
 
    private void createPanel(JPanel panel, int c, int r) {
 
        JPanel p = new JPanel();
        p.setLayout(new GridLayout(3, 3));
        p.setBorder(new LineBorder(new Color(0, 0, 0)));
        panel.add(p);
        for (int y = 1; y <= 3; y++) {
            for (int x = 1; x <= 3; x++) {
                SudokuLabel label = new SudokuLabel(x + ((c - 1) * 3), y + (r - 1) * 3);
                p.add(label);
                label.addMouseListener(this);
            }
        }
    }
 
    private class SudokuLabel extends JLabel {
 
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private int x;
        private int y;
 
        public SudokuLabel(int x, int y) {
            this.x = x;
            this.y = y;
            setOpaque(true);
            setBackground(Color.WHITE);
            setBorder(new LineBorder(Color.gray));
            setHorizontalAlignment(SwingConstants.CENTER);
            setToolTipText("");
        }
 
        @Override
        public String getToolTipText() {
            return possible[x][y];
        }
 
        @Override
        public Color getForeground() {
            if (gameStarted && currentNumber == board[x][y]) {
                return Color.red;
            }
            return super.getForeground();
        }
 
        @Override
        public Color getBackground() {
            if (!gameStarted) {
                return Color.lightGray;
            } else {
                if (initial[x][y]) {
                    return Color.lightGray;
                } else {
                    return Color.white;
                }
            }
        }
 
        @Override
        public String getText() {
            if (!gameStarted) {
                return x + "," + y;
            } else {
                if (board[x][y] == 0) {
                    return "";
                }
                return "" + board[x][y];
            }
        }
    }
 
    private class SudokuFileFilter extends FileFilter {
 
        @Override
        public boolean accept(File f) {
            if (f.isDirectory()) {
                return true;
            }
            String str = f.getName();
            try {
                String ext = str.substring(str.lastIndexOf('.') + 1);
                if (ext.equalsIgnoreCase("sdo")) {
                    return true;
                }
            } catch (Exception e) {
            }
            return false;
        }
 
        @Override
        public String getDescription() {
            return "Sudoku file";
        }
 
    }
 
    @Override
    public void actionPerformed(ActionEvent event) {
        String ac = event.getActionCommand();
        try {
            int num = Integer.parseInt(ac);
            currentNumber = num;
            repaint();
        } catch (Exception e) {
            currentNumber = 0;
            repaint();
        }
 
    }
 
    @Override
    public void mouseClicked(MouseEvent e) {
    }
 
    @Override
    public void mouseEntered(MouseEvent e) {
    }
 
    @Override
    public void mouseExited(MouseEvent e) {
    }
 
    @Override
    public void mousePressed(MouseEvent e) {
    }
 
    @Override
    public void mouseReleased(MouseEvent e) {
        if (e.getSource() instanceof SudokuLabel && gameStarted) {
            SudokuLabel l = (SudokuLabel) e.getSource();
            setValue(l.x, l.y, currentNumber);
        }
    }
 
    private void setValue(int x, int y, int value) {
        if (!initial[x][y]) {
            if (value == 0) {
                addToUndo(x, y, board[x][y], value);
                board[x][y] = value;
                textArea.append("Clearing [" + x + "," + y + "]\n");
                updatePossibleValues();
                repaint();
 
            } else if (isValidMove(x, y, value)) {
                addToUndo(x, y, board[x][y], value);
                board[x][y] = value;
                textArea.append("Setting [" + x + "," + y + "] to " + value + "\n");
                updatePossibleValues();
                repaint();
 
                if (isGameComplete()) {
                    gameComplete = true;
                    JOptionPane.showMessageDialog(this, "Game complete ", "Well Done!", JOptionPane.INFORMATION_MESSAGE);
                }
            } else {
                textArea.append("Invalid move [" + x + "," + y + "] to " + value + "\n");
            }
            updateButtons();
        }
    }
 
    private void updateButtons() {
        updateButtonColor(jb1, 1);
        updateButtonColor(jb2, 2);
        updateButtonColor(jb3, 3);
        updateButtonColor(jb4, 4);
        updateButtonColor(jb5, 5);
        updateButtonColor(jb6, 6);
        updateButtonColor(jb7, 7);
        updateButtonColor(jb8, 8);
        updateButtonColor(jb9, 9);
    }
 
    private void updatePossibleValues() {
        for (int y = 1; y <= 9; y++) {
            for (int x = 1; x <= 9; x++) {
                if (board[x][y] == 0) {
                    List<Integer> poss = getPossibleValues(x, y);
                    String possValues = "";
                    for (int pv : poss) {
                        possValues += pv;
                    }
                    possible[x][y] = possValues;
                } else {
                    possible[x][y] = "";
                }
            }
        }
    }
 
    private void addToUndo(int x, int y, int o, int n) {
        undoMoves.push(new SudokuMove(x, y, o, n));
    }
 
    private void updateButtonColor(JButton jb, int i) {
        if (isNumberComplete(i)) {
            jb.setBackground(Color.lightGray);
        } else {
            jb.setBackground(UIManager.getColor("Button.background"));
        }
 
    }
 
    private boolean isNumberComplete(int i) {
        int count = 0;
        for (int y = 1; y <= 9; y++) {
            for (int x = 1; x <= 9; x++) {
                if (board[x][y] == i) {
                    count++;
                }
            }
        }
        if (count == 9) {
            return true;
        }
        return false;
    }
 
    private boolean isGameComplete() {
        for (int y = 1; y <= 9; y++) {
            for (int x = 1; x <= 9; x++) {
                if (board[x][y] == 0) {
                    return false;
                }
            }
        }
        return true;
    }
 
    private boolean isValidMove(int x, int y, int value) {
        // Check column
        for (int row = 1; row <= 9; row++) {
            int curVal = board[x][row];
            if (curVal == value) {
                return false;
            }
        }
        // Check row
        for (int col = 1; col <= 9; col++) {
            if (board[col][y] == value) {
                return false;
            }
        }
        // Check mini grid
        int sc = x - ((x - 1) % 3);
        int sr = y - ((y - 1) % 3);
        for (int col = 0; col <= 2; col++) {
            for (int row = 0; row <= 2; row++) {
                if (board[sc + col][sr + row] == value) {
                    return false;
                }
            }
        }
        return true;
    }
 
    public static void main(String[] args) {
        SudokuApplet applet = new SudokuApplet();
        JFrame jf = new JFrame();
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setSize(500, 400);
        jf.getContentPane().add(applet);
        jf.setResizable(false);
        jf.setVisible(true);
    }
 
    private class SudokuMove {
        int x, y;
        int newValue;
        int oldValue;
 
        public SudokuMove(int x, int y, int oldValue, int newValue) {
            this.x = x;
            this.y = y;
            this.oldValue = oldValue;
            this.newValue = newValue;
        }
 
    }
}
Sudoku java applet