Was working on sudoku puzzles during easter holiday. Became bored, and being the nerd I am decided to practice my swing and created a tool to help solve it. Keep in mind that this is a “quickie” job, created in couple days work. Just extract jSudokuSolver.zip and run:
java -jar jSudoku.jar
Comes with one predefined sample.
Feel free to use the source for whatever you wish. If you want you can clone the git repo http://github.com/kenglxn/JSudoku:
git clone git://github.com/kenglxn/JSudoku.git
I based my implementation on a sudoku puzzle found in a magazine. I picked a puzzle from the magazine and made this the sample. Then i wrote a test to confirm the solver worked for the two axis, and the sub-grid. Then the last test to confirm the solver get’s the expected solution. Code for the test is pasted below.
Heres the sample:
And here’s the sample solved:
This is the recursive method that does all the work:
void solvePuzzle(Integer numberOfEmptyCellsBefore) {
createWorkingSetOfShallowCopyCells();
calculatePossibleValuesAndSetCellValues();
commitChangesToAllCells();
Integer numberOfEmptyCells = getNumberOfEmptyCellsInGrid();
if (!numberOfEmptyCells.equals(numberOfEmptyCellsBefore)) {
solvePuzzle(numberOfEmptyCells);
}
}
Heres a paste of the junit test that i used to build the logic:
package net.glxn.sudoku;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
public class JSudokuTestCase {
@Test
public void shouldSolveforXSibling() {
String expected = "9";
JSudoku jSudoku = new JSudoku();
ArrayList<Cell> xSiblings = jSudoku.textField9.xSiblings;
ArrayList<String> stringList = new ArrayList<String>();
stringList.addAll(Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8"));
for (Cell xSib : xSiblings) {
xSib.setText(stringList.get(0));
stringList.remove(0);
}
jSudoku.solvePuzzle(jSudoku.getNumberOfEmptyCellsInGrid());
Assert.assertEquals(expected, jSudoku.textField9.getText());
}
@Test
public void shouldSolveforYSibling() {
String expected = "9";
JSudoku jSudoku = new JSudoku();
ArrayList<Cell> ysiblings = jSudoku.textField9.ySiblings;
ArrayList<String> stringList = new ArrayList<String>();
stringList.addAll(Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8"));
for (Cell ySib : ysiblings) {
ySib.setText(stringList.get(0));
stringList.remove(0);
}
jSudoku.solvePuzzle(jSudoku.getNumberOfEmptyCellsInGrid());
Assert.assertEquals(expected, jSudoku.textField9.getText());
}
@Test
public void shouldSolveforSubGridSibling() {
String expected = "9";
JSudoku jSudoku = new JSudoku();
ArrayList<Cell> ysiblings = jSudoku.textField9.subGridSiblings;
ArrayList<String> stringList = new ArrayList<String>();
stringList.addAll(Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8"));
for (Cell ySib : ysiblings) {
ySib.setText(stringList.get(0));
stringList.remove(0);
}
jSudoku.solvePuzzle(jSudoku.getNumberOfEmptyCellsInGrid());
Assert.assertEquals(expected, jSudoku.textField9.getText());
}
@Test
public void shouldBeAbleToSolveSample() {
JSudoku jSudoku = new JSudoku();
jSudoku.loadSample();
confirmSample(jSudoku);
jSudoku.solvePuzzle(jSudoku.getNumberOfEmptyCellsInGrid());
confirmWholeGrid(jSudoku);
}
private void confirmSample(JSudoku jSudoku) {
confirmExpectedValueAtCoord(jSudoku, 2, 1, 1);
confirmExpectedValueAtCoord(jSudoku, 4, 1, 9);
confirmExpectedValueAtCoord(jSudoku, 5, 1, 5);
confirmExpectedValueAtCoord(jSudoku, 7, 1, 7);
confirmExpectedValueAtCoord(jSudoku, 2, 2, 5);
confirmExpectedValueAtCoord(jSudoku, 5, 2, 3);
confirmExpectedValueAtCoord(jSudoku, 2, 3, 9);
confirmExpectedValueAtCoord(jSudoku, 4, 3, 6);
confirmExpectedValueAtCoord(jSudoku, 9, 3, 5);
confirmExpectedValueAtCoord(jSudoku, 1, 4, 2);
confirmExpectedValueAtCoord(jSudoku, 3, 4, 6);
confirmExpectedValueAtCoord(jSudoku, 6, 4, 8);
confirmExpectedValueAtCoord(jSudoku, 3, 5, 8);
confirmExpectedValueAtCoord(jSudoku, 7, 5, 4);
confirmExpectedValueAtCoord(jSudoku, 4, 6, 3);
confirmExpectedValueAtCoord(jSudoku, 7, 6, 6);
confirmExpectedValueAtCoord(jSudoku, 9, 6, 9);
confirmExpectedValueAtCoord(jSudoku, 1, 7, 4);
confirmExpectedValueAtCoord(jSudoku, 6, 7, 3);
confirmExpectedValueAtCoord(jSudoku, 8, 7, 5);
confirmExpectedValueAtCoord(jSudoku, 5, 8, 8);
confirmExpectedValueAtCoord(jSudoku, 8, 8, 6);
confirmExpectedValueAtCoord(jSudoku, 3, 9, 1);
confirmExpectedValueAtCoord(jSudoku, 5, 9, 4);
confirmExpectedValueAtCoord(jSudoku, 6, 9, 5);
confirmExpectedValueAtCoord(jSudoku, 8, 9, 2);
}
private void confirmWholeGrid(JSudoku jSudoku) {
confirmExpectedValueAtCoord(jSudoku, 1, 1, 8);
confirmExpectedValueAtCoord(jSudoku, 2, 1, 1);
confirmExpectedValueAtCoord(jSudoku, 3, 1, 2);
confirmExpectedValueAtCoord(jSudoku, 4, 1, 9);
confirmExpectedValueAtCoord(jSudoku, 5, 1, 5);
confirmExpectedValueAtCoord(jSudoku, 6, 1, 4);
confirmExpectedValueAtCoord(jSudoku, 7, 1, 7);
confirmExpectedValueAtCoord(jSudoku, 8, 1, 3);
confirmExpectedValueAtCoord(jSudoku, 9, 1, 6);
confirmExpectedValueAtCoord(jSudoku, 1, 2, 6);
confirmExpectedValueAtCoord(jSudoku, 2, 2, 5);
confirmExpectedValueAtCoord(jSudoku, 3, 2, 4);
confirmExpectedValueAtCoord(jSudoku, 4, 2, 8);
confirmExpectedValueAtCoord(jSudoku, 5, 2, 3);
confirmExpectedValueAtCoord(jSudoku, 6, 2, 7);
confirmExpectedValueAtCoord(jSudoku, 7, 2, 2);
confirmExpectedValueAtCoord(jSudoku, 8, 2, 9);
confirmExpectedValueAtCoord(jSudoku, 9, 2, 1);
confirmExpectedValueAtCoord(jSudoku, 1, 3, 7);
confirmExpectedValueAtCoord(jSudoku, 2, 3, 9);
confirmExpectedValueAtCoord(jSudoku, 3, 3, 3);
confirmExpectedValueAtCoord(jSudoku, 4, 3, 6);
confirmExpectedValueAtCoord(jSudoku, 5, 3, 2);
confirmExpectedValueAtCoord(jSudoku, 6, 3, 1);
confirmExpectedValueAtCoord(jSudoku, 7, 3, 8);
confirmExpectedValueAtCoord(jSudoku, 8, 3, 4);
confirmExpectedValueAtCoord(jSudoku, 9, 3, 5);
confirmExpectedValueAtCoord(jSudoku, 1, 4, 2);
confirmExpectedValueAtCoord(jSudoku, 2, 4, 7);
confirmExpectedValueAtCoord(jSudoku, 3, 4, 6);
confirmExpectedValueAtCoord(jSudoku, 4, 4, 4);
confirmExpectedValueAtCoord(jSudoku, 5, 4, 9);
confirmExpectedValueAtCoord(jSudoku, 6, 4, 8);
confirmExpectedValueAtCoord(jSudoku, 7, 4, 5);
confirmExpectedValueAtCoord(jSudoku, 8, 4, 1);
confirmExpectedValueAtCoord(jSudoku, 9, 4, 3);
confirmExpectedValueAtCoord(jSudoku, 1, 5, 9);
confirmExpectedValueAtCoord(jSudoku, 2, 5, 3);
confirmExpectedValueAtCoord(jSudoku, 3, 5, 8);
confirmExpectedValueAtCoord(jSudoku, 4, 5, 5);
confirmExpectedValueAtCoord(jSudoku, 5, 5, 1);
confirmExpectedValueAtCoord(jSudoku, 6, 5, 6);
confirmExpectedValueAtCoord(jSudoku, 7, 5, 4);
confirmExpectedValueAtCoord(jSudoku, 8, 5, 7);
confirmExpectedValueAtCoord(jSudoku, 9, 5, 2);
confirmExpectedValueAtCoord(jSudoku, 1, 6, 1);
confirmExpectedValueAtCoord(jSudoku, 2, 6, 4);
confirmExpectedValueAtCoord(jSudoku, 3, 6, 5);
confirmExpectedValueAtCoord(jSudoku, 4, 6, 3);
confirmExpectedValueAtCoord(jSudoku, 5, 6, 7);
confirmExpectedValueAtCoord(jSudoku, 6, 6, 2);
confirmExpectedValueAtCoord(jSudoku, 7, 6, 6);
confirmExpectedValueAtCoord(jSudoku, 8, 6, 8);
confirmExpectedValueAtCoord(jSudoku, 9, 6, 9);
confirmExpectedValueAtCoord(jSudoku, 1, 7, 4);
confirmExpectedValueAtCoord(jSudoku, 2, 7, 8);
confirmExpectedValueAtCoord(jSudoku, 3, 7, 9);
confirmExpectedValueAtCoord(jSudoku, 4, 7, 2);
confirmExpectedValueAtCoord(jSudoku, 5, 7, 6);
confirmExpectedValueAtCoord(jSudoku, 6, 7, 3);
confirmExpectedValueAtCoord(jSudoku, 7, 7, 1);
confirmExpectedValueAtCoord(jSudoku, 8, 7, 5);
confirmExpectedValueAtCoord(jSudoku, 9, 7, 7);
confirmExpectedValueAtCoord(jSudoku, 1, 8, 5);
confirmExpectedValueAtCoord(jSudoku, 2, 8, 2);
confirmExpectedValueAtCoord(jSudoku, 3, 8, 7);
confirmExpectedValueAtCoord(jSudoku, 4, 8, 1);
confirmExpectedValueAtCoord(jSudoku, 5, 8, 8);
confirmExpectedValueAtCoord(jSudoku, 6, 8, 9);
confirmExpectedValueAtCoord(jSudoku, 7, 8, 3);
confirmExpectedValueAtCoord(jSudoku, 8, 8, 6);
confirmExpectedValueAtCoord(jSudoku, 9, 8, 4);
confirmExpectedValueAtCoord(jSudoku, 1, 9, 3);
confirmExpectedValueAtCoord(jSudoku, 2, 9, 6);
confirmExpectedValueAtCoord(jSudoku, 3, 9, 1);
confirmExpectedValueAtCoord(jSudoku, 4, 9, 7);
confirmExpectedValueAtCoord(jSudoku, 5, 9, 4);
confirmExpectedValueAtCoord(jSudoku, 6, 9, 5);
confirmExpectedValueAtCoord(jSudoku, 7, 9, 9);
confirmExpectedValueAtCoord(jSudoku, 8, 9, 2);
confirmExpectedValueAtCoord(jSudoku, 9, 9, 8);
}
private void confirmExpectedValueAtCoord(JSudoku jSudoku, int x, int y, Integer expected) {
Assert.assertEquals("Confirm ["+x+","+y+"] = "+ expected, expected, jSudoku.getCellAtCoord(x, y, jSudoku.allCells).getIntValue());
}