mirror of
https://github.com/ulfgebhardt/java_sudoku.git
synced 2025-12-12 17:15:49 +00:00
init commit
This commit is contained in:
commit
08008df617
8
.classpath
Normal file
8
.classpath
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/acm"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
17
.project
Normal file
17
.project
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>Sudoku</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
12
.settings/org.eclipse.jdt.core.prefs
Normal file
12
.settings/org.eclipse.jdt.core.prefs
Normal file
@ -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
|
||||
73
build.xml
Normal file
73
build.xml
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- You may freely edit this file. See commented blocks below for -->
|
||||
<!-- some examples of how to customize the build. -->
|
||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||
<!-- the Compile on Save feature is turned off for the project. -->
|
||||
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||
<!-- in the project's Project Properties dialog box.-->
|
||||
<project name="Sudoku" default="default" basedir=".">
|
||||
<description>Builds, tests, and runs the project Sudoku.</description>
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
<!--
|
||||
|
||||
There exist several targets which are by default empty and which can be
|
||||
used for execution of your tasks. These targets are usually executed
|
||||
before and after some main targets. They are:
|
||||
|
||||
-pre-init: called before initialization of project properties
|
||||
-post-init: called after initialization of project properties
|
||||
-pre-compile: called before javac compilation
|
||||
-post-compile: called after javac compilation
|
||||
-pre-compile-single: called before javac compilation of single file
|
||||
-post-compile-single: called after javac compilation of single file
|
||||
-pre-compile-test: called before javac compilation of JUnit tests
|
||||
-post-compile-test: called after javac compilation of JUnit tests
|
||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||
-pre-jar: called before JAR building
|
||||
-post-jar: called after JAR building
|
||||
-post-clean: called after cleaning build products
|
||||
|
||||
(Targets beginning with '-' are not intended to be called on their own.)
|
||||
|
||||
Example of inserting an obfuscator after compilation could look like this:
|
||||
|
||||
<target name="-post-compile">
|
||||
<obfuscate>
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</obfuscate>
|
||||
</target>
|
||||
|
||||
For list of available properties check the imported
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
|
||||
Another way to customize the build is by overriding existing main targets.
|
||||
The targets of interest are:
|
||||
|
||||
-init-macrodef-javac: defines macro for javac compilation
|
||||
-init-macrodef-junit: defines macro for junit execution
|
||||
-init-macrodef-debug: defines macro for class debugging
|
||||
-init-macrodef-java: defines macro for class execution
|
||||
-do-jar: JAR building
|
||||
run: execution of project
|
||||
-javadoc-build: Javadoc generation
|
||||
test-report: JUnit report generation
|
||||
|
||||
An example of overriding the target for project execution could look like this:
|
||||
|
||||
<target name="run" depends="Sudoku-impl.jar">
|
||||
<exec dir="bin" executable="launcher.exe">
|
||||
<arg file="${dist.jar}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
Notice that the overridden target depends on the jar target and not only on
|
||||
the compile target as the regular run target does. Again, for a list of available
|
||||
properties which you can use, check the target you are overriding in the
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
-->
|
||||
</project>
|
||||
BIN
build/classes/Sudoku$1.class
Normal file
BIN
build/classes/Sudoku$1.class
Normal file
Binary file not shown.
BIN
build/classes/Sudoku.class
Normal file
BIN
build/classes/Sudoku.class
Normal file
Binary file not shown.
BIN
build/classes/SudokuTest.class
Normal file
BIN
build/classes/SudokuTest.class
Normal file
Binary file not shown.
BIN
libs/acm.jar
Normal file
BIN
libs/acm.jar
Normal file
Binary file not shown.
3
manifest.mf
Normal file
3
manifest.mf
Normal file
@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
X-COMMENT: Main-Class will be added automatically by build
|
||||
|
||||
1401
nbproject/build-impl.xml
Normal file
1401
nbproject/build-impl.xml
Normal file
File diff suppressed because it is too large
Load Diff
8
nbproject/genfiles.properties
Normal file
8
nbproject/genfiles.properties
Normal file
@ -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
|
||||
1
nbproject/private/.gitignore
vendored
Normal file
1
nbproject/private/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/private.properties
|
||||
77
nbproject/project.properties
Normal file
77
nbproject/project.properties
Normal file
@ -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}
|
||||
13
nbproject/project.xml
Normal file
13
nbproject/project.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||
<type>org.netbeans.modules.java.j2seproject</type>
|
||||
<configuration>
|
||||
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<name>Sudoku</name>
|
||||
<source-roots>
|
||||
<root id="src.dir"/>
|
||||
</source-roots>
|
||||
<test-roots/>
|
||||
</data>
|
||||
</configuration>
|
||||
</project>
|
||||
465
src/Sudoku.java
Normal file
465
src/Sudoku.java
Normal file
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
228
src/SudokuTest.java
Normal file
228
src/SudokuTest.java
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user