commit 08008df617aedfd8383c4d6557584b5a1511b17f Author: Ulf Gebhardt Date: Sat Feb 27 10:22:48 2016 +0100 init commit diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..06c7b69 --- /dev/null +++ b/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..b273577 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + Sudoku + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..8d01539 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +#Tue Jan 19 13:27:34 CET 2010 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..1d8722f --- /dev/null +++ b/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project Sudoku. + + + diff --git a/build/classes/Sudoku$1.class b/build/classes/Sudoku$1.class new file mode 100644 index 0000000..3868265 Binary files /dev/null and b/build/classes/Sudoku$1.class differ diff --git a/build/classes/Sudoku.class b/build/classes/Sudoku.class new file mode 100644 index 0000000..b152302 Binary files /dev/null and b/build/classes/Sudoku.class differ diff --git a/build/classes/SudokuTest.class b/build/classes/SudokuTest.class new file mode 100644 index 0000000..eb9ee4f Binary files /dev/null and b/build/classes/SudokuTest.class differ diff --git a/libs/acm.jar b/libs/acm.jar new file mode 100644 index 0000000..0548ea2 Binary files /dev/null and b/libs/acm.jar differ diff --git a/manifest.mf b/manifest.mf new file mode 100644 index 0000000..328e8e5 --- /dev/null +++ b/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..792b89b --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..ac8c1a0 --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=43255cb9 +build.xml.script.CRC32=501d6b1d +build.xml.stylesheet.CRC32=8064a381@1.78.1.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=43255cb9 +nbproject/build-impl.xml.script.CRC32=0975d34f +nbproject/build-impl.xml.stylesheet.CRC32=2d327b5d@1.78.1.48 diff --git a/nbproject/private/.gitignore b/nbproject/private/.gitignore new file mode 100644 index 0000000..7b6be20 --- /dev/null +++ b/nbproject/private/.gitignore @@ -0,0 +1 @@ +/private.properties diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..afc2374 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,77 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processor.options= +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/Sudoku.jar +dist.javadoc.dir=${dist.dir}/javadoc +excludes= +file.reference.acm.jar=libs\\acm.jar +file.reference.Sudoku-src=src +includes=** +jar.compress=false +javac.classpath=\ + ${file.reference.acm.jar}:\ + ${libs.junit_4.classpath} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.external.vm=true +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=Sudoku +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=${file.reference.Sudoku-src} diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..96dfc72 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,13 @@ + + + org.netbeans.modules.java.j2seproject + + + Sudoku + + + + + + + diff --git a/src/Sudoku.java b/src/Sudoku.java new file mode 100644 index 0000000..38552ed --- /dev/null +++ b/src/Sudoku.java @@ -0,0 +1,465 @@ +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import java.util.Arrays; + +import javax.swing.BorderFactory; +import javax.swing.JButton; + +import acm.gui.IntField; +import acm.gui.TableLayout; +import acm.gui.TablePanel; +import acm.program.Program; + +public class Sudoku extends Program { + + /** + * Fixed boardsize - alter this if you want to make smaller/bigger boards + * Not tested! (is this possible?^^) + */ + public static int boardsize = 9; + + /** + * Simple avoiding some warnings + */ + private static final long serialVersionUID = 1L; + + + // the model of the game board + IntField[][] board; + + /** + * Construct a new game of Sudoku. + * + * Initializes the game board with all zeroes. + */ + public Sudoku() { + super(); + board = new IntField[9][9]; + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + board[i][j] = new IntField(0); + } + } + } + + /** + * Initialize a new game of Sudoku with a given configuration. + * + * @param values + * The configuration of the game board. + */ + public Sudoku(int[][] values) { + this(); + setConfiguration(values); + } + + /** + * Initialize the view component. + */ + @Override + public void init() { + setTitle("Sudoku"); + setLayout(new TableLayout(4, 3)); + for (int i = 0; i < 9; i++) { + add(assembleInnerTable(i)); + } + add(new JButton("Solve")); + addActionListeners(); + } + + /** + * Assemble a single 3x3 field. + * + * @param n + * The number of the 3x3 field to assemble a table component for. + * @return The TablePanel containing a 3x3 field. + */ + private TablePanel assembleInnerTable(int n) { + TablePanel tp = new TablePanel(3, 3); + for (int i = 0; i < 9; i++) { + // we assemble 3x3 field-wise and have to adjust array indices + // accordingly + int row = 3 * (n / 3) + i / 3; + int col = 3 * (n % 3) + i % 3; + + // The constructor made sure these are instantiated IntFields + IntField intField = board[row][col]; + + // Register a KeyListener to suppress non-digit entries + intField.addKeyListener(new KeyListener() { + @Override + public void keyPressed(KeyEvent e) { + // don't care + } + + @Override + public void keyReleased(KeyEvent e) { + // don't care + } + + @Override + public void keyTyped(KeyEvent e) { + try { + // try to parse the pressed keys value into an Integer + Integer.parseInt(String.valueOf(e.getKeyChar())); + } catch (NumberFormatException nfe) { + // consume the event and stop it from propagating + e.consume(); + } + } + }); + tp.add(intField); + } + // draw a solid black border around every 3x3 field + tp.setBorder(BorderFactory.createLineBorder(Color.black)); + return tp; + } + + /** + * Initialize the game board with the given arrays of ints. + * + * @param init The 9x9 two-dimensional array with int values from 0..9 + */ + public void setConfiguration(int[][] init) { + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (init[i][j] > 0 && init[i][j] <= 9) { + board[i][j].setValue(init[i][j]); + } else { + board[i][j].setValue(0); + } + } + } + } + + /** + * Return the current configuration of the game board. + * + * @return The current configuration. + */ + public int[][] getConfiguration() { + int[][] tmp = new int[9][9]; + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + tmp[i][j] = board[i][j].getValue(); + } + } + return tmp; + } + + // if no solution was found, color every field red + public void colorForFailure() { + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + board[i][j].setBackground(new Color(255, 0, 0)); + } + } + } + + // if there was a solution, color the new values fields green + public void colorForSuccess(int[][] solution) { + int[][] actual = getConfiguration(); + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (solution[i][j] != actual[i][j]) { + board[i][j].setBackground(new Color(0, 255, 0)); + board[i][j].setValue(solution[i][j]); + } else { + board[i][j].setBackground(new Color(255, 255, 255)); + } + } + } + } + + /** + * The ActionListeners method to process pressed buttons. + */ + @Override + public void actionPerformed(ActionEvent e) { + if (e.getActionCommand().equals("Solve")) { + // The solve button was pressed, find a solution + int[][] solution = solve(getConfiguration()); + if (solution != null) { + colorForSuccess(solution); + } else { + colorForFailure(); + } + } + } + + /** + * Checks if board is valid. Returns false if it is valid, else true. + * + * public because requested + * + * @param board Sudoku Configuration + * @return false if valid, true if not. + */ + public boolean reject(int[][] board) + { + //Check input + if(board == null) + { + return true; + } + + { //Check if 1-9 is max 1x in every row & line + + //Helper-Vars - Start with false + boolean[] row = new boolean[boardsize]; + boolean[] line = new boolean[boardsize]; + + for(int i=0; i < boardsize;i++) //rows + { + for(int j=0; j < boardsize; j++) //lines + { + { //row + int t = board[i][j]; //value @ act pos //row + + if( t > 0 && //skip 0 and wrong input! + t <= boardsize) + { + if(row[t-1]) //already seen? + { + //Number already found in row + return true; + } else + { + //Set to true - do not accept this number a second time. + row[t-1] = true; + } + } + } //row + + { //line + int t = board[j][i]; //value @ act pos //line + + if( t > 0 && //skip 0 and wrong input! + t <= boardsize) + { + if(line[t-1]) //already seen? + { + //Number already found in line + return true; + } else + { + //Set to true - do not accept this number a second time. + line[t-1] = true; + } + } + } //line + } + + //Reset helper-vars, new line/row + Arrays.fill(row, false); + Arrays.fill(line, false); + } + } //Check if 1-9 is max 1x in every row&line + + { //Check board-3x3-blocks + + //Helper-Var - Starts with false + boolean[] block = new boolean[boardsize]; + + for(int n=0; n < 3; n++) //Count block-lines + { + for(int m=0; m < 3; m++) //Count block-rows + { + for(int i=0; i < (boardsize/3); i++) //Count line within block + { + for(int j=0; j < (boardsize/3); j++) //Count row within block + { + int t = board[n*3+i][m*3+j]; //Value @ act pos + + if( t > 0 && //skip 0 and wrong input! + t <= boardsize) + { + if(block[t-1]) //Number already seen? + { + //Number already found in Block + return true; + } else + { + //Set to true - do not accept this number a second time. + block[t-1] = true; + } + } + } + } + + //Reset - new block + Arrays.fill(block, false); + } + } + } //Check board-3x3-blocks + + + //All Checked & OK + return false; + } + + /** + * Returns position of the next field which is 0 (free field). + * + * Position-Encoding: row*boardsize + line + * row and line starting by 0. + * first item is 0, + * last item is boardsize*boardsize -1 + * + * public because assumed it is requested + * + * @param board + * @return Position of next free field or -1 if no free field was found. + */ + public int getNextFreeField(int[][] board) + { + //Just Checking - should never happen, but method is public... you know... + if(board == null) + { + return -1; + } + + //Search + for(int i=0; i < boardsize; i++) //row + { + for(int j=0; j < boardsize; j++) //line + { + if(board[i][j] == 0) //Is act pos 0 ? + { + return i*boardsize + j; //Position Calculation + } + } + } + + //Field has no empty fields + return -1; + } + + /** + * Returns new Sudoku configuration with altered (incremented by 1) + * value at given position pos. + * + * public because assumed it is requested + * + * @param board Given Sudoku configuration + * @param pos position to be altered + * @return Sudoku configuration or null + */ + public int[][] getNextExtension(int[][] board, int pos) + { + //Decrypt 1-dim-pos to 2-dim-pos + int i = pos/boardsize; + int j = pos-i*boardsize; + + if( board != null && //Board ok? + i < boardsize && //row ok? + j < boardsize && //line ok? + board[i][j] < boardsize) //value @ pos < boardsize? + { + int[][] newboard = new int[boardsize][boardsize]; + + { //clone board -> newboard + for(int k = 0; k < boardsize; k++) + { + System.arraycopy(board[k], 0, newboard[k], 0, board[k].length); + } + } //clone board -> newboard + + newboard[i][j] += 1; // inc value @ pos -> +1 + return newboard; //return the new board + } + + return null; //Something was wrong - see if clause + } + + /** + * Solve the given Sudoku configuration and return the result. + * + * @param configuration A 9x9 two-dimensional array, columns first. + * @return The solution for this game of Sudoku or null if not solvable. + */ + public int[][] solve(int[][] configuration) + { + return solve(configuration,getNextFreeField(configuration)); + } + /** + * Solve the given Sudoku configuration and return the result. + * + * @param configuration A 9x9 two-dimensional array, columns first. + * @param position to be altered (start with getNextFreeField(configuration)) + * @return The solution for this game of Sudoku or null if not solvable. + */ + public int[][] solve(int[][] configuration, int pos) + { + //Recursion-anchor + if(pos == -1) //all fields are done, or startcall was wrong + { + return configuration; + } + + //Recursion + while(true) //Always do^^ + { + configuration = getNextExtension(configuration,pos); //getNextExt checks if pos/config is valid! + + if(configuration == null) + { + return null; //Could not alter act pos any further, not solvable, try alter fields before filled in + } + + if(!reject(configuration)) //is generated config ok? + { + int[][] tboard = solve(configuration,getNextFreeField(configuration)); //Fill in empty fields recursivly. + + if(tboard != null) // was solvable + { + return tboard; + } //else config was not solvable - alter fields filled in before. + } + } + } + + /** + * Main-Method + * + * some start-soduko-defs + * + * @param args + */ + public static void main(final String[] args) { + @SuppressWarnings("unused") + final int[][] emptyField = new int[][] { { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; + + @SuppressWarnings("unused") + final int[][] fullField = new int[][] { { 5, 3, 4, 6, 7, 8, 9, 1, 2 }, + { 6, 7, 2, 1, 9, 5, 3, 4, 8 }, { 1, 9, 8, 3, 4, 2, 5, 6, 7 }, + { 8, 5, 9, 7, 6, 1, 4, 2, 3 }, { 4, 2, 6, 8, 5, 3, 7, 9, 1 }, + { 7, 1, 3, 9, 2, 4, 8, 5, 6 }, { 9, 6, 1, 5, 3, 7, 2, 8, 4 }, + { 2, 8, 7, 4, 1, 9, 6, 3, 5 }, { 3, 4, 5, 2, 8, 6, 1, 7, 9 } }; + + final int[][] actualField1 = new int[][] { + { 5, 3, 0, 0, 7, 0, 0, 0, 0 }, { 6, 0, 0, 1, 9, 5, 0, 0, 0 }, + { 0, 9, 8, 0, 0, 0, 0, 6, 0 }, { 8, 0, 0, 0, 6, 0, 0, 0, 3 }, + { 4, 0, 0, 8, 0, 3, 0, 0, 1 }, { 7, 0, 0, 0, 2, 0, 0, 0, 6 }, + { 0, 6, 0, 0, 0, 0, 2, 8, 0 }, { 0, 0, 0, 4, 1, 9, 0, 0, 5 }, + { 0, 0, 0, 0, 8, 0, 0, 7, 9 } }; + + @SuppressWarnings("unused") + final int[][] actualField2 = new int[][] { + { 1, 0, 2, 0, 0, 0, 0, 0, 0 }, { 0, 0, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 4 }, { 0, 4, 0, 0, 5, 0, 0, 0, 0 }, + { 0, 6, 0, 0, 7, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 2, 0 }, + { 0, 8, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 8, 0, 0 } }; + + new Sudoku(actualField1).start(); + } + +} diff --git a/src/SudokuTest.java b/src/SudokuTest.java new file mode 100644 index 0000000..3f8d974 --- /dev/null +++ b/src/SudokuTest.java @@ -0,0 +1,228 @@ +import java.util.Arrays; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; + +public class SudokuTest { + + /** + * Test cases for the solver. Public, because you can also use them to use + * them for game initialization! + */ + public final static int[][] testCase1 = new int[][] { + { 5, 3, 0, 0, 7, 0, 0, 0, 0 }, { 6, 0, 0, 1, 9, 5, 0, 0, 0 }, + { 0, 9, 8, 0, 0, 0, 0, 6, 0 }, { 8, 0, 0, 0, 6, 0, 0, 0, 3 }, + { 4, 0, 0, 8, 0, 3, 0, 0, 1 }, { 7, 0, 0, 0, 2, 0, 0, 0, 6 }, + { 0, 6, 0, 0, 0, 0, 2, 8, 0 }, { 0, 0, 0, 4, 1, 9, 0, 0, 5 }, + { 0, 0, 0, 0, 8, 0, 0, 7, 9 } }; + + public final static int[][] testCase1Result = new int[][] { + { 5, 3, 4, 6, 7, 8, 9, 1, 2 }, { 6, 7, 2, 1, 9, 5, 3, 4, 8 }, + { 1, 9, 8, 3, 4, 2, 5, 6, 7 }, { 8, 5, 9, 7, 6, 1, 4, 2, 3 }, + { 4, 2, 6, 8, 5, 3, 7, 9, 1 }, { 7, 1, 3, 9, 2, 4, 8, 5, 6 }, + { 9, 6, 1, 5, 3, 7, 2, 8, 4 }, { 2, 8, 7, 4, 1, 9, 6, 3, 5 }, + { 3, 4, 5, 2, 8, 6, 1, 7, 9 } }; + + public final static int[][] testCase2 = new int[][] { + { 1, 0, 2, 0, 0, 0, 0, 0, 0 }, { 0, 0, 3, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 4 }, { 0, 4, 0, 0, 5, 0, 0, 0, 0 }, + { 0, 6, 0, 0, 7, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 2, 0 }, + { 0, 8, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 8, 0, 0 } }; + + public final static int[][] testCase2Result = new int[][] { + { 1, 5, 2, 3, 4, 6, 7, 8, 9 }, { 4, 7, 3, 1, 8, 9, 2, 5, 6 }, + { 6, 9, 8, 5, 2, 7, 1, 3, 4 }, { 2, 4, 1, 6, 5, 3, 9, 7, 8 }, + { 5, 6, 9, 2, 7, 8, 3, 4, 1 }, { 8, 3, 7, 4, 9, 1, 6, 2, 5 }, + { 3, 8, 4, 9, 1, 2, 5, 6, 7 }, { 7, 1, 6, 8, 3, 5, 4, 9, 2 }, + { 9, 2, 5, 7, 6, 4, 8, 1, 3 } }; + + final int[][] testField3 = new int[][] { { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 1, 2, 0, 4, 5, 6, 7 }, + { 0, 0, 2, 0, 0, 0, 4, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 4, 0, 0, 0, 2, 0, 0 }, { 0, 0, 5, 4, 0, 2, 1, 0, 0 }, + { 0, 0, 6, 0, 0, 0, 0, 0, 0 }, { 0, 0, 7, 0, 0, 0, 0, 0, 0 } }; + + //From template + public final int[][] fullField = new int[][] { { 5, 3, 4, 6, 7, 8, 9, 1, 2 }, + { 6, 7, 2, 1, 9, 5, 3, 4, 8 }, { 1, 9, 8, 3, 4, 2, 5, 6, 7 }, + { 8, 5, 9, 7, 6, 1, 4, 2, 3 }, { 4, 2, 6, 8, 5, 3, 7, 9, 1 }, + { 7, 1, 3, 9, 2, 4, 8, 5, 6 }, { 9, 6, 1, 5, 3, 7, 2, 8, 4 }, + { 2, 8, 7, 4, 1, 9, 6, 3, 5 }, { 3, 4, 5, 2, 8, 6, 1, 7, 9 } }; + + final int[][] sudoku17_1 = new int[][] { + {0, 0, 0, 0, 0, 0, 0, 1, 0}, { 0, 6, 0, 3, 0, 0, 0, 0, 0}, + {9, 0, 0, 0, 0, 0, 0, 7, 0}, { 1, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 4, 0, 0, 6, 0, 0}, { 0, 0, 6, 0, 0, 0, 5, 0, 3}, + {0, 0, 0, 0, 9, 0, 0, 0, 0}, { 0, 2, 0, 0, 0, 0, 4, 0, 0}, + {0, 0, 8, 0, 1, 7, 0, 0, 0} + }; + + /** + * The test instance. + */ + private Sudoku sudoku; + + /** + * Sets up the test environment. + */ + @Before + public void setUp() { + sudoku = new Sudoku(); + } + + /** + * Timing check + */ + /* + //This one is quite nasty + @Test + public void testTimingField17() + { + Assert.assertNull(sudoku.solve(sudoku17_1)); + }*/ + + /** + * Timing check2 + */ + @Test + public void testTimingField3() + { + Assert.assertNull(sudoku.solve(testField3)); + } + + /** + * Test if predefined fullfield is ok. + */ + @Test + public void testFullField() + { + //Question was raised - so i do the test + Assert.assertFalse(sudoku.reject(fullField)); + } + + /** + * Tests the {@link Sudoku#reject(int[][])} method. + */ + @Test + public void testReject() { + int[][] config = new int[9][9]; + + // Empty field is always valid! + Assert.assertFalse("reject false negative: rejected empty field!", + sudoku.reject(config)); + + // Check rows + config = new int[9][9]; + config[5][1] = 9; + config[5][7] = 9; + Assert.assertTrue("reject false negative: row check failed!", sudoku + .reject(config)); + + config[5][7] = 6; + Assert.assertFalse("reject false positive: row check failed!", sudoku + .reject(config)); + + // Check columns + config = new int[9][9]; + config[5][1] = 9; + config[8][1] = 9; + Assert.assertTrue("reject false negative: column check failed!", sudoku + .reject(config)); + + config[8][1] = 3; + Assert.assertFalse("reject false positive: column check failed!", + sudoku.reject(config)); + + // Check 3x3 fields + config = new int[9][9]; + config[0][0] = 7; + config[2][2] = 7; + Assert.assertTrue("reject false negative: 3x3 field check failed!", + sudoku.reject(config)); + + config[2][2] = 4; + Assert.assertFalse("reject false positive: 3x3 field check failed!", + sudoku.reject(config)); + + config[2][3] = 7; // out of top-left field + Assert.assertFalse("reject false positive: 3x3 field check failed!", + sudoku.reject(config)); + + // Check with real-world example + Assert.assertFalse("reject false positive: real world example", sudoku + .reject(testCase1Result)); + } + + /** + * Tests the {@link Sudoku#getNextFreeField(int[][])} method. + */ + @Test + public void testNextFreeField() { + int[][] config = new int[9][9]; + + // Full & empty field + Assert.assertEquals( + "getNextFreeField failed: should return 0 if field is empty!", + 0, sudoku.getNextFreeField(config)); + Assert.assertEquals( + "getNextFreeField failed: should return -1 if field is full!", + -1, sudoku.getNextFreeField(testCase1Result)); + + // Normal fields + config[0][0] = 5; + Assert.assertEquals("getNextFreeField failed!", 1, sudoku + .getNextFreeField(config)); + config[0][1] = 5; + Assert.assertEquals("getNextFreeField failed!", 2, sudoku + .getNextFreeField(config)); + config[0] = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + Assert.assertEquals("getNextFreeField failed for second row!", 9, + sudoku.getNextFreeField(config)); + } + + /** + * Tests the {@link Sudoku#getNextExtension(int[][], int)} method. + */ + @Test + public void testNextExtension() { + int[][] config = new int[9][9]; + config[4][6] = 8; // Field 42 + + int[][] newConfig = sudoku.getNextExtension(config, 42); + Assert.assertEquals( + "getNextExtension failed: did not increment (correct) field!", + 9, newConfig[4][6]); + Assert.assertNotSame( + "getNextExtension failed: did not create a new array!", config, + newConfig); + Assert.assertNotSame( + "getNextExtension failed: did not clone sub-arrays!", + config[0], newConfig[0]); + + newConfig = sudoku.getNextExtension(newConfig, 42); + Assert + .assertEquals( + "getNextExtension failed: did not return null if field already is 9", + null, newConfig); + } + + /** + * Tests the {@link Sudoku#solve(int[][])} method. + */ + @Test + public void testSolve() { + // Test cases + Assert.assertTrue("solve failed for test case 1", Arrays.deepEquals( + sudoku.solve(testCase1), testCase1Result)); + + Assert.assertTrue("solve failed for test case 2", Arrays.deepEquals( + sudoku.solve(testCase2), testCase2Result)); + + // Finished field should not be modified + Assert.assertTrue("solve failed for finished field", Arrays.deepEquals( + sudoku.solve(testCase1Result), testCase1Result)); + } +}