# HG changeset patch # User Mike Becker # Date 1595678488 -7200 # Node ID 576e7a2861aea3e4901088c4078b3b84842f06f4 # Parent e70a0e3555fbd30e9ef528c66853664c7522fd99 converts to maven project diff -r e70a0e3555fb -r 576e7a2861ae .hgignore --- a/.hgignore Fri Feb 01 10:18:47 2013 +0100 +++ b/.hgignore Sat Jul 25 14:01:28 2020 +0200 @@ -3,3 +3,5 @@ \.chg\..*$ \.rej$ \.conflict\~$ +.iml$ +.idea/ diff -r e70a0e3555fb -r 576e7a2861ae build.xml --- a/build.xml Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ - - - - - - - - - - - Builds, tests, and runs the project Sudoku. - - - diff -r e70a0e3555fb -r 576e7a2861ae nbproject/build-impl.xml --- a/nbproject/build-impl.xml Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1400 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Must set src.dir - Must set test.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 -cp "${run.classpath.with.dist.jar}" ${main.class} - - - - - - - - - - - - - - - - - - - - - - - - - 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 -r e70a0e3555fb -r 576e7a2861ae nbproject/genfiles.properties --- a/nbproject/genfiles.properties Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -build.xml.data.CRC32=7c7a460d -build.xml.script.CRC32=0dfebe00 -build.xml.stylesheet.CRC32=28e38971@1.53.1.46 -# 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=7c7a460d -nbproject/build-impl.xml.script.CRC32=2bd010ed -nbproject/build-impl.xml.stylesheet.CRC32=6ddba6b6@1.53.1.46 diff -r e70a0e3555fb -r 576e7a2861ae nbproject/project.properties --- a/nbproject/project.properties Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -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} -# 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= -includes=** -jar.compress=false -javac.classpath= -# Space-separated list of extra javac options -javac.compilerargs= -javac.deprecation=false -javac.processorpath=\ - ${javac.classpath} -javac.source=1.7 -javac.target=1.7 -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=de.uapcore.sudoku.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=src -test.src.dir=test diff -r e70a0e3555fb -r 576e7a2861ae nbproject/project.xml --- a/nbproject/project.xml Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ - - - org.netbeans.modules.java.j2seproject - - - Sudoku - - - - - - - - - diff -r e70a0e3555fb -r 576e7a2861ae pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pom.xml Sat Jul 25 14:01:28 2020 +0200 @@ -0,0 +1,35 @@ + + 4.0.0 + + de.uapcore + sudoku + 1.0 + + + + + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.0.2 + + + + + de.uapcore.sudoku.Sudoku + + + + + + + + + diff -r e70a0e3555fb -r 576e7a2861ae src/de/uapcore/sudoku/ActionHandler.java --- a/src/de/uapcore/sudoku/ActionHandler.java Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,204 +0,0 @@ -/* - * Copyright 2013 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package de.uapcore.sudoku; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.IOException; -import javax.swing.JFileChooser; -import javax.swing.JOptionPane; - -/** - * - * @author mike - */ -public final class ActionHandler implements ActionListener { - - public static final String SAVE = "save"; - public static final String CHECK = "check"; - public static final String SOLVE = "solve"; - - public static final String NEW = "new"; - public static final String OPEN = "open"; - public static final String SAVE_AS = "save as"; - public static final String QUIT = "quit"; - public static final String ABOUT = "about"; - - private Field field; - private Solver solver; - private DocumentHandler doc; - - public ActionHandler(Field f) { - field = f; - solver = new Solver(); - doc = new DocumentHandler(); - } - - private boolean chooseSaveFilename() { - JFileChooser fc = new JFileChooser("."); - fc.setMultiSelectionEnabled(false); - if (fc.showSaveDialog(field) == JFileChooser.APPROVE_OPTION) { - File f = fc.getSelectedFile(); - if (f.exists()) { - int result = JOptionPane.showConfirmDialog(field, - "Bereits existierende Datei überschreiben?", "Sudoku", - JOptionPane.YES_NO_OPTION); - if (result == JOptionPane.YES_OPTION) { - doc.setFilename(f.getAbsolutePath()); - return true; - } else { - return false; - } - } else { - doc.setFilename(f.getAbsolutePath()); - return true; - } - } else { - return false; - } - } - - private void open() { - JFileChooser fc = new JFileChooser("."); - fc.setMultiSelectionEnabled(false); - if (fc.showOpenDialog(field) == JFileChooser.APPROVE_OPTION) { - File f = fc.getSelectedFile(); - doc.setFilename(f.getAbsolutePath()); - try { - doc.load(field); - } catch (IOException e) { - JOptionPane.showMessageDialog(field, - "Datei konnte nicht geladen werden: "+e.getMessage(), - "Sudoku", JOptionPane.ERROR_MESSAGE); - } - } - } - - private boolean save(boolean rename) { - if (!doc.isFilenameSet() || rename) { - if (!chooseSaveFilename()) { - return false; - } - } - if (solver.check(field)) { - field.setAllCellsModified(false); - try { - doc.save(field); - } catch (IOException e) { - JOptionPane.showMessageDialog(field, - "Datei konnte nicht gespeichert werden: "+e.getMessage(), - "Sudoku", JOptionPane.ERROR_MESSAGE); - } - return true; - } else { - JOptionPane.showMessageDialog(field, - "Das Feld kann mit Fehlern nicht gespeichert werden!", - "Sudoku", JOptionPane.ERROR_MESSAGE); - return false; - } - } - - private void check() { - if (solver.check(field)) { - JOptionPane.showMessageDialog(field, "Überprüfung erfolgreich!", - "Sudoku", JOptionPane.INFORMATION_MESSAGE); - } else { - JOptionPane.showMessageDialog(field, "Das Feld enthält Fehler!", - "Sudoku", JOptionPane.WARNING_MESSAGE); - } - } - - private void solve() { - if (!solver.check(field) || !solver.solve(field)) { - JOptionPane.showMessageDialog(field, "Das Feld ist nicht lösbar!", - "Sudoku", JOptionPane.WARNING_MESSAGE); - } - } - - private boolean saveUnsaved() { - boolean proceed = false; - if (field.isAnyCellModified()) { - int result = JOptionPane.showConfirmDialog(field, - "Das Feld ist ungespeichert - jetzt speichern?", - "Sudoku", JOptionPane.YES_NO_CANCEL_OPTION); - if (result == JOptionPane.YES_OPTION) { - if (save(false)) { - proceed = true; - } - } else if (result == JOptionPane.NO_OPTION) { - proceed = true; - } - } else { - proceed = true; - } - - return proceed; - } - - @Override - public void actionPerformed(ActionEvent e) { - switch (e.getActionCommand()) { - case NEW: - if (saveUnsaved()) { - doc.clearFilename(); - field.clear(); - } - break; - case OPEN: - open(); - break; - case SAVE: - save(false); - break; - case SAVE_AS: - save(true); - break; - case CHECK: - check(); - break; - case SOLVE: - solve(); - break; - case QUIT: - if (saveUnsaved()) { - System.exit(0); - } - break; - case ABOUT: - JOptionPane.showMessageDialog(field, - "Sudoku - Copyright (c) 2013 Mike Becker\nwww.uap-core.de"+ - "\nPublished under the BSD License", - "Sudoku", JOptionPane.INFORMATION_MESSAGE); - break; - default: - throw new UnsupportedOperationException( - "unknown action: "+e.getActionCommand()); - } - } - -} diff -r e70a0e3555fb -r 576e7a2861ae src/de/uapcore/sudoku/ButtonPanel.java --- a/src/de/uapcore/sudoku/ButtonPanel.java Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright 2013 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package de.uapcore.sudoku; - -import java.awt.Color; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import javax.swing.JButton; -import javax.swing.JPanel; - -/** - * - * @author mike - */ -public final class ButtonPanel extends JPanel { - - private JButton save, check, solve; - - public ButtonPanel(ActionHandler l) { - setLayout(new GridBagLayout()); - - GridBagConstraints c = new GridBagConstraints(); - c.insets = new Insets(10, 10, 10, 10); - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - - c.gridx = 0; - c.gridy = 0; - save = new JButton("Speichern"); - add(save, c); - c.gridx++; - check = new JButton("Prüfen"); - add(check, c); - solve = new JButton("Lösen"); - c.gridx++; - add(solve, c); - - save.setActionCommand(ActionHandler.SAVE); - save.addActionListener(l); - check.setActionCommand(ActionHandler.CHECK); - check.addActionListener(l); - solve.setActionCommand(ActionHandler.SOLVE); - solve.addActionListener(l); - - setBackground(Color.WHITE); - } -} diff -r e70a0e3555fb -r 576e7a2861ae src/de/uapcore/sudoku/DocumentHandler.java --- a/src/de/uapcore/sudoku/DocumentHandler.java Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -/* - * Copyright 2013 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package de.uapcore.sudoku; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * - * @author mike - */ -public class DocumentHandler { - - private String filename; - - public void load(Field field) throws IOException { - if (!isFilenameSet()) { - throw new IOException("no filename supplied"); - } - int row = 0; - try (BufferedReader in = new BufferedReader( - new InputStreamReader(new FileInputStream(filename)))) { - Pattern pat = Pattern.compile("^\\s*(?:[1-9_] ){8}[1-9_]\\s*$"); - String line; - while ((line = in.readLine()) != null) { - if (line.matches("^\\s*$")) { - continue; - } - Matcher m = pat.matcher(line); - if (m.matches()) { - String c[] = line.trim().split(" "); - if (c.length != 9) { - break; - } - for (int i = 0 ; i < 9 ; i++) { - field.setCellValue(i, row, - c[i].equals("_") ? 0 : Integer.valueOf(c[i])); - } - row++; - } else { - break; - } - } - if (row != 9) { - throw new IOException("Kein Sudoku-Feld enthalten!"); - } - } - field.setAllCellsModified(false); - } - - public void save(Field field) throws IOException { - if (!isFilenameSet()) { - throw new IOException("no filename supplied"); - } - - try (BufferedWriter out = new BufferedWriter( - new OutputStreamWriter(new FileOutputStream(filename)))) { - for (int i = 0 ; i < 9 ; i++) { - int[] row = field.getRow(i); - for (int j = 0 ; j < 9 ; j++) { - out.append(row[j] > 0 ? Character.forDigit(row[j], 10):'_'); - out.append(j == 8 ? '\n': ' '); - } - } - } - } - - public void setFilename(String filename) { - this.filename = filename; - } - - public void clearFilename() { - filename = null; - } - - public boolean isFilenameSet() { - return filename != null; - } -} diff -r e70a0e3555fb -r 576e7a2861ae src/de/uapcore/sudoku/Field.java --- a/src/de/uapcore/sudoku/Field.java Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,182 +0,0 @@ -/* - * Copyright 2013 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package de.uapcore.sudoku; - -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.image.BufferedImage; -import javax.swing.JPanel; - -/** - * - * @author mike - */ -public final class Field extends JPanel { - private SudokuTextField[][] cells; - - public Field() { - setBackground(Color.WHITE); - - setLayout(new GridBagLayout()); - GridBagConstraints c = new GridBagConstraints(); - c.insets = new Insets(5, 5, 5, 5); - - cells = new SudokuTextField[9][9]; - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - cells[x][y] = new SudokuTextField(); - c.gridx = x; - c.gridy = y; - add(cells[x][y], c); - } - } - } - - @Override - public void paint(Graphics graphics) { - final int w = getWidth(); - final int h = getHeight(); - final int cw = w / 9; - final int ch = h / 9; - - BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); - Graphics2D g = img.createGraphics(); - g.setBackground(Color.WHITE); - g.clearRect(0, 0, w, h); - - g.setColor(Color.BLACK); - g.drawRect(1, 1, w-2, h-2); - g.drawRect(2, 2, w-4, h-4); - for (int x = cw ; x < w ; x += cw) { - for (int y = ch ; y < h ; y += ch) { - g.drawLine(x, 2, x, h-2); - g.drawLine(2, y, w-2, y); - if ((x / cw) % 3 == 0) { - g.drawLine(x+1, 2, x+1, h-2); - } - if ((y / ch) % 3 == 0) { - g.drawLine(2, y+1, w-2, y+1); - } - } - } - - graphics.drawImage(img, 0, 0, this); - super.paintChildren(graphics); - } - - public boolean isCellEmpty(int x, int y) { - return getCellValue(x, y) == 0; - } - - public int getCellValue(int x, int y) { - return cells[x][y].getValue(); - } - - public void setCellValue(int x, int y, int v) { - cells[x][y].setValue(v); - } - - public void clearCellValue(int x, int y) { - setCellValue(x, y, 0); - } - - public void setCellModified(int x, int y, boolean modified) { - cells[x][y].setModified(modified); - } - - public void setAllCellsModified(boolean modified) { - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - cells[x][y].setModified(modified); - } - } - } - - public boolean isAnyCellModified() { - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - if (cells[x][y].isModified()) { - return true; - } - } - } - return false; - } - - public void clear() { - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - cells[x][y].setValue(0); - } - } - } - - public int[][] getSquare(int x, int y) { - if (x < 0 || x > 2 || y < 0 || y > 2) { - throw new IllegalArgumentException("Invalid square coordinates"); - } - int[][] square = new int[3][3]; - - for (int u = 0 ; u < 3 ; u++) { - for (int v = 0 ; v < 3 ; v++) { - square[u][v] = getCellValue(3*x+u, 3*y+v); - } - } - - return square; - } - - public int[] getRow(int y) { - if (y < 0 || y > 8) { - throw new IllegalArgumentException("Invalid row number"); - } - int row[] = new int[9]; - - for (int x = 0 ; x < 9 ; x++) { - row[x] = getCellValue(x, y); - } - - return row; - } - - public int[] getColumn(int x) { - if (x < 0 || x > 8) { - throw new IllegalArgumentException("Invalid column number"); - } - int column[] = new int[9]; - - for (int y = 0 ; y < 9 ; y++) { - column[y] = getCellValue(x, y); - } - - return column; - } -} diff -r e70a0e3555fb -r 576e7a2861ae src/de/uapcore/sudoku/MainMenu.java --- a/src/de/uapcore/sudoku/MainMenu.java Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,101 +0,0 @@ -/* - * Copyright 2013 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package de.uapcore.sudoku; - -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.KeyStroke; - -/** - * - * @author mike - */ -public class MainMenu { - - private ActionHandler handler; - private JMenuBar menuBar; - - public MainMenu(ActionHandler h) { - handler = h; - menuBar = new JMenuBar(); - menuBar.add(createMenu("Datei", 'd', - createMenuItem("Neu", 'n', "control N", ActionHandler.NEW), - createMenuItem("Öffnen", 'f', "control O", ActionHandler.OPEN), - createMenuItem("Speichern", 's', "control S", ActionHandler.SAVE), - createMenuItem("Speichern als...", 'a', ActionHandler.SAVE_AS), - createSeparator(), - createMenuItem("Prüfen", 'p', "control P", ActionHandler.CHECK), - createMenuItem("Lösen", 'l', "control L", ActionHandler.SOLVE), - createSeparator(), - createMenuItem("Beenden", 'e', ActionHandler.QUIT) - )); - menuBar.add(createMenu("Help", 'h', - createMenuItem("Über...", 'b', "F1", ActionHandler.ABOUT) - )); - } - - private JMenuItem createSeparator() { - // Return null, the createMenu method knows how to handle it - return null; - } - - private JMenu createMenu(String caption, char mnemonic, JMenuItem...items) { - JMenu menu = new JMenu(caption); - menu.setMnemonic(mnemonic); - for (JMenuItem item : items) { - if (item == null) { - menu.addSeparator(); - } else { - menu.add(item); - } - } - return menu; - } - - private JMenuItem createMenuItem(String caption, char mnemonic, - String command) { - return createMenuItem(caption, mnemonic, null, command); - } - - private JMenuItem createMenuItem(String caption, char mnemonic, - String stroke, String command) { - JMenuItem item = new JMenuItem(caption); - item.setMnemonic(mnemonic); - if (stroke != null) { - item.setAccelerator(KeyStroke.getKeyStroke(stroke)); - } - item.setActionCommand(command); - item.addActionListener(handler); - - return item; - } - - public JMenuBar getMenuBar() { - return menuBar; - } -} diff -r e70a0e3555fb -r 576e7a2861ae src/de/uapcore/sudoku/Solver.java --- a/src/de/uapcore/sudoku/Solver.java Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,227 +0,0 @@ -/* - * Copyright 2013 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package de.uapcore.sudoku; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author mike - */ -public final class Solver { - - public Solver() { - } - - private Integer fillInCandidate(Field f, List[][] candidates, int x, int y) { - Integer c = candidates[x][y].remove(0); - f.setCellValue(x, y, c); - f.setCellModified(x, y, true); - for (int i = 0 ; i < 9 ; i++) { - candidates[x][i].remove(c); - candidates[i][y].remove(c); - } - for (int i = 0 ; i < 3 ; i++) { - for (int j = 0 ; j < 3 ; j++) { - candidates[x-x%3+i][y-y%3+j].remove(c); - } - } - return c; - } - - private void makeBackup(List[][] candidates, List[][] candidatesBackup) { - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - candidatesBackup[x][y] = new ArrayList<>(9); - candidatesBackup[x][y].addAll(candidates[x][y]); - } - } - } - - private void makeBackup(Field f, int[][] fieldBackup) { - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - fieldBackup[x][y] = f.getCellValue(x, y); - } - } - } - - private void restoreBackup(Field f, int[][] fieldBackup) { - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - f.setCellValue(x, y, fieldBackup[x][y]); - } - } - } - - private void restoreBackup(List[][] candidates, List[][] candidatesBackup) { - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - candidates[x][y].clear(); - candidates[x][y].addAll(candidatesBackup[x][y]); - } - } - } - - private boolean solve(Field f, List[][] candidates) { - - // Make backup - List[][] candidatesBackup = new List[9][9]; - int[][] fieldBackup = new int[9][9]; - makeBackup(candidates, candidatesBackup); - makeBackup(f, fieldBackup); - - // Fill in distinct solutions - boolean fillDistinct; - do { - fillDistinct = false; - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - if (f.isCellEmpty(x, y) && candidates[x][y].size() == 1) { - fillInCandidate(f, candidates, x, y); - fillDistinct = true; - } - } - } - } while (fillDistinct); - - // Try out remaining candidates - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - if (f.isCellEmpty(x, y)) { - while (candidates[x][y].size() > 0) { - List[][] cb = new List[9][9]; - makeBackup(candidates, cb); - Integer c = fillInCandidate(f, candidates, x, y); - if (solve(f, candidates)) { - break; - } else { - f.clearCellValue(x, y); - restoreBackup(candidates, cb); - // Remove current candidate anyway - candidates[x][y].remove(c); - } - } - } - if (f.isCellEmpty(x, y)) { - restoreBackup(f, fieldBackup); - restoreBackup(candidates, candidatesBackup); - return false; - } - } - } - - return true; - } - - public boolean solve(Field f) { - - // Calculate initial candidates - List candidates[][] = new List[9][9]; - for (int x = 0 ; x < 9 ; x++) { - for (int y = 0 ; y < 9 ; y++) { - candidates[x][y] = new ArrayList<>(9); - if (f.getCellValue(x, y) == 0) { - // All numbers are candidates - for (int c = 1 ; c <= 9 ; c++) { - candidates[x][y].add(c); - } - // Remove row duplicates - int[] line = f.getRow(y); - for (Integer c : line) { - candidates[x][y].remove(c); - } - // Remove column duplicates - line = f.getColumn(x); - for (Integer c : line) { - candidates[x][y].remove(c); - } - // Remove square duplicates - int[][] square = f.getSquare(x/3, y/3); - for (int[] sq : square) { - for (Integer c : sq) { - candidates[x][y].remove(c); - } - } - } - } - } - - // Backtrack - return solve(f, candidates); - } - - public boolean check(Field f) { - int line[]; - for (int i = 0 ; i < 9 ; i++) { - line = f.getRow(i); - if (!valid(line)) { - return false; - } - line = f.getColumn(i); - if (!valid(line)) { - return false; - } - } - - int square[][]; - for (int x = 0 ; x < 3 ; x++) { - for (int y = 0 ; y < 3 ; y++) { - square = f.getSquare(x, y); - if (!valid(square)) { - return false; - } - } - } - - return true; - } - - private boolean valid(int[] line) { - int numbers[]; - numbers = new int[9]; - for (int i = 0 ; i < 9 ; i++) { - int l = line[i]-1; - if (l >= 0) { - if ((++numbers[l]) > 1) { - return false; - } - } - } - - return true; - } - - private boolean valid(int[][] square) { - int[] line = new int[9]; - for (int x = 0 ; x < 3 ; x++) { - System.arraycopy(square[x], 0, line, 3*x, 3); - } - return valid(line); - } -} diff -r e70a0e3555fb -r 576e7a2861ae src/de/uapcore/sudoku/Sudoku.java --- a/src/de/uapcore/sudoku/Sudoku.java Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -/* - * Copyright 2013 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package de.uapcore.sudoku; - -import java.awt.Color; -import java.awt.Container; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import javax.swing.JFrame; - -/** - * - * @author mike - */ -public final class Sudoku extends JFrame { - - public Sudoku() { - super("Sudoku"); - - Field f = new Field(); - ActionHandler h = new ActionHandler(f); - setJMenuBar(new MainMenu(h).getMenuBar()); - - Container content = getContentPane(); - - content.setLayout(new GridBagLayout()); - GridBagConstraints c = new GridBagConstraints(); - c.insets = new Insets(20, 20, 20, 20); - c.fill = GridBagConstraints.HORIZONTAL; - - c.gridx = 0; c.gridy = 0; - content.add(f, c); - c.gridy++; - content.add(new ButtonPanel(h), c); - - pack(); - content.setBackground(Color.WHITE); - setLocationByPlatform(true); - setDefaultCloseOperation(EXIT_ON_CLOSE); - } - - /** - * @param args the command line arguments - */ - public static void main(String[] args) { - new Sudoku().setVisible(true); - } -} diff -r e70a0e3555fb -r 576e7a2861ae src/de/uapcore/sudoku/SudokuTextField.java --- a/src/de/uapcore/sudoku/SudokuTextField.java Fri Feb 01 10:18:47 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -/* - * Copyright 2013 Mike Becker. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package de.uapcore.sudoku; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import javax.swing.JTextField; - -/** - * - * @author mike - */ -public final class SudokuTextField extends JTextField { - - private boolean modified; - - public SudokuTextField() { - setBorder(null); - setBackground(Color.WHITE); - - setFont(new Font("Dialog", Font.PLAIN, 18)); - setHorizontalAlignment(JTextField.CENTER); - - Dimension dim = new Dimension(40,40); - setPreferredSize(dim); - setMinimumSize(dim); - setMaximumSize(dim); - - addKeyListener(new KeyAdapter() { - private void handle(KeyEvent e) { - char c = e.getKeyChar(); - if (!e.isAltDown() && !e.isControlDown() && - !Character.isISOControl(c)) { - // Perform clean input check - if (getText().length() > 0 && getSelectedText() == null) { - if (c != KeyEvent.CHAR_UNDEFINED) { - e.consume(); - } - } else { - if (c < '1' || c > '9') { - e.consume(); - } else { - setModified(true); - } - } - } else { - // We can still be tricked by hotkeys, so do it the hard way - if (!getText().matches("^[1-9]$")) { - setText(""); - } - } - } - - @Override - public void keyPressed(KeyEvent e) { - handle(e); - } - - @Override - public void keyTyped(KeyEvent e) { - handle(e); - } - - @Override - public void keyReleased(KeyEvent e) { - handle(e); - } - - }); - addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - selectAll(); - } - @Override - public void focusLost(FocusEvent e) { - select(0, 0); - } - }); - } - - public int getValue() { - if (getText().length() > 0) { - return Integer.valueOf(getText()); - } else { - return 0; - } - } - - public void setValue(int v) { - if (v == 0) { - setText(""); - } else if (v < 10) { - setText(String.valueOf(v)); - } else { - throw new IllegalArgumentException( - "Sudoku numbers must be in range 0-9 (0 means 'not set')"); - } - } - - public void setModified(boolean modified) { - this.modified = modified; - setForeground(modified?Color.BLUE:Color.BLACK); - } - - public boolean isModified() { - return modified; - } -} diff -r e70a0e3555fb -r 576e7a2861ae src/main/java/de/uapcore/sudoku/ActionHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/sudoku/ActionHandler.java Sat Jul 25 14:01:28 2020 +0200 @@ -0,0 +1,203 @@ +/* + * Copyright 2013 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package de.uapcore.sudoku; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; + +/** + * + * @author mike + */ +public final class ActionHandler implements ActionListener { + + public static final String SAVE = "save"; + public static final String CHECK = "check"; + public static final String SOLVE = "solve"; + + public static final String NEW = "new"; + public static final String OPEN = "open"; + public static final String SAVE_AS = "save as"; + public static final String QUIT = "quit"; + public static final String ABOUT = "about"; + + private Field field; + private Solver solver; + private DocumentHandler doc; + + public ActionHandler(Field f) { + field = f; + solver = new Solver(); + doc = new DocumentHandler(); + } + + private boolean chooseSaveFilename() { + JFileChooser fc = new JFileChooser("."); + fc.setMultiSelectionEnabled(false); + if (fc.showSaveDialog(field) == JFileChooser.APPROVE_OPTION) { + File f = fc.getSelectedFile(); + if (f.exists()) { + int result = JOptionPane.showConfirmDialog(field, + "Bereits existierende Datei überschreiben?", "Sudoku", + JOptionPane.YES_NO_OPTION); + if (result == JOptionPane.YES_OPTION) { + doc.setFilename(f.getAbsolutePath()); + return true; + } else { + return false; + } + } else { + doc.setFilename(f.getAbsolutePath()); + return true; + } + } else { + return false; + } + } + + private void open() { + JFileChooser fc = new JFileChooser("."); + fc.setMultiSelectionEnabled(false); + if (fc.showOpenDialog(field) == JFileChooser.APPROVE_OPTION) { + File f = fc.getSelectedFile(); + doc.setFilename(f.getAbsolutePath()); + try { + doc.load(field); + } catch (IOException e) { + JOptionPane.showMessageDialog(field, + "Datei konnte nicht geladen werden: "+e.getMessage(), + "Sudoku", JOptionPane.ERROR_MESSAGE); + } + } + } + + private boolean save(boolean rename) { + if (!doc.isFilenameSet() || rename) { + if (!chooseSaveFilename()) { + return false; + } + } + if (solver.check(field)) { + field.setAllCellsModified(false); + try { + doc.save(field); + } catch (IOException e) { + JOptionPane.showMessageDialog(field, + "Datei konnte nicht gespeichert werden: "+e.getMessage(), + "Sudoku", JOptionPane.ERROR_MESSAGE); + } + return true; + } else { + JOptionPane.showMessageDialog(field, + "Das Feld kann mit Fehlern nicht gespeichert werden!", + "Sudoku", JOptionPane.ERROR_MESSAGE); + return false; + } + } + + private void check() { + if (solver.check(field)) { + JOptionPane.showMessageDialog(field, "Überprüfung erfolgreich!", + "Sudoku", JOptionPane.INFORMATION_MESSAGE); + } else { + JOptionPane.showMessageDialog(field, "Das Feld enthält Fehler!", + "Sudoku", JOptionPane.WARNING_MESSAGE); + } + } + + private void solve() { + if (!solver.check(field) || !solver.solve(field)) { + JOptionPane.showMessageDialog(field, "Das Feld ist nicht lösbar!", + "Sudoku", JOptionPane.WARNING_MESSAGE); + } + } + + private boolean saveUnsaved() { + boolean proceed = false; + if (field.isAnyCellModified()) { + int result = JOptionPane.showConfirmDialog(field, + "Das Feld ist ungespeichert - jetzt speichern?", + "Sudoku", JOptionPane.YES_NO_CANCEL_OPTION); + if (result == JOptionPane.YES_OPTION) { + if (save(false)) { + proceed = true; + } + } else if (result == JOptionPane.NO_OPTION) { + proceed = true; + } + } else { + proceed = true; + } + + return proceed; + } + + @Override + public void actionPerformed(ActionEvent e) { + switch (e.getActionCommand()) { + case NEW: + if (saveUnsaved()) { + doc.clearFilename(); + field.clear(); + } + break; + case OPEN: + open(); + break; + case SAVE: + save(false); + break; + case SAVE_AS: + save(true); + break; + case CHECK: + check(); + break; + case SOLVE: + solve(); + break; + case QUIT: + if (saveUnsaved()) { + System.exit(0); + } + break; + case ABOUT: + JOptionPane.showMessageDialog(field, + "Sudoku - Copyright (c) 2013 Mike Becker\nwww.uap-core.de"+ + "\nPublished under the BSD License", + "Sudoku", JOptionPane.INFORMATION_MESSAGE); + break; + default: + throw new UnsupportedOperationException( + "unknown action: "+e.getActionCommand()); + } + } + +} diff -r e70a0e3555fb -r 576e7a2861ae src/main/java/de/uapcore/sudoku/ButtonPanel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/sudoku/ButtonPanel.java Sat Jul 25 14:01:28 2020 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright 2013 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package de.uapcore.sudoku; + +import javax.swing.*; +import java.awt.*; + +/** + * + * @author mike + */ +public final class ButtonPanel extends JPanel { + + private JButton save, check, solve; + + public ButtonPanel(ActionHandler l) { + setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(10, 10, 10, 10); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + + c.gridx = 0; + c.gridy = 0; + save = new JButton("Speichern"); + add(save, c); + c.gridx++; + check = new JButton("Prüfen"); + add(check, c); + solve = new JButton("Lösen"); + c.gridx++; + add(solve, c); + + save.setActionCommand(ActionHandler.SAVE); + save.addActionListener(l); + check.setActionCommand(ActionHandler.CHECK); + check.addActionListener(l); + solve.setActionCommand(ActionHandler.SOLVE); + solve.addActionListener(l); + + setBackground(Color.WHITE); + } +} diff -r e70a0e3555fb -r 576e7a2861ae src/main/java/de/uapcore/sudoku/DocumentHandler.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/sudoku/DocumentHandler.java Sat Jul 25 14:01:28 2020 +0200 @@ -0,0 +1,104 @@ +/* + * Copyright 2013 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package de.uapcore.sudoku; + +import java.io.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * + * @author mike + */ +public class DocumentHandler { + + private String filename; + + public void load(Field field) throws IOException { + if (!isFilenameSet()) { + throw new IOException("no filename supplied"); + } + int row = 0; + try (BufferedReader in = new BufferedReader( + new InputStreamReader(new FileInputStream(filename)))) { + Pattern pat = Pattern.compile("^\\s*(?:[1-9_] ){8}[1-9_]\\s*$"); + String line; + while ((line = in.readLine()) != null) { + if (line.matches("^\\s*$")) { + continue; + } + Matcher m = pat.matcher(line); + if (m.matches()) { + String c[] = line.trim().split(" "); + if (c.length != 9) { + break; + } + for (int i = 0 ; i < 9 ; i++) { + field.setCellValue(i, row, + c[i].equals("_") ? 0 : Integer.valueOf(c[i])); + } + row++; + } else { + break; + } + } + if (row != 9) { + throw new IOException("Kein Sudoku-Feld enthalten!"); + } + } + field.setAllCellsModified(false); + } + + public void save(Field field) throws IOException { + if (!isFilenameSet()) { + throw new IOException("no filename supplied"); + } + + try (BufferedWriter out = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(filename)))) { + for (int i = 0 ; i < 9 ; i++) { + int[] row = field.getRow(i); + for (int j = 0 ; j < 9 ; j++) { + out.append(row[j] > 0 ? Character.forDigit(row[j], 10):'_'); + out.append(j == 8 ? '\n': ' '); + } + } + } + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public void clearFilename() { + filename = null; + } + + public boolean isFilenameSet() { + return filename != null; + } +} diff -r e70a0e3555fb -r 576e7a2861ae src/main/java/de/uapcore/sudoku/Field.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/sudoku/Field.java Sat Jul 25 14:01:28 2020 +0200 @@ -0,0 +1,177 @@ +/* + * Copyright 2013 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package de.uapcore.sudoku; + +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; + +/** + * + * @author mike + */ +public final class Field extends JPanel { + private SudokuTextField[][] cells; + + public Field() { + setBackground(Color.WHITE); + + setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(5, 5, 5, 5); + + cells = new SudokuTextField[9][9]; + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + cells[x][y] = new SudokuTextField(); + c.gridx = x; + c.gridy = y; + add(cells[x][y], c); + } + } + } + + @Override + public void paint(Graphics graphics) { + final int w = getWidth(); + final int h = getHeight(); + final int cw = w / 9; + final int ch = h / 9; + + BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + Graphics2D g = img.createGraphics(); + g.setBackground(Color.WHITE); + g.clearRect(0, 0, w, h); + + g.setColor(Color.BLACK); + g.drawRect(1, 1, w-2, h-2); + g.drawRect(2, 2, w-4, h-4); + for (int x = cw ; x < w ; x += cw) { + for (int y = ch ; y < h ; y += ch) { + g.drawLine(x, 2, x, h-2); + g.drawLine(2, y, w-2, y); + if ((x / cw) % 3 == 0) { + g.drawLine(x+1, 2, x+1, h-2); + } + if ((y / ch) % 3 == 0) { + g.drawLine(2, y+1, w-2, y+1); + } + } + } + + graphics.drawImage(img, 0, 0, this); + super.paintChildren(graphics); + } + + public boolean isCellEmpty(int x, int y) { + return getCellValue(x, y) == 0; + } + + public int getCellValue(int x, int y) { + return cells[x][y].getValue(); + } + + public void setCellValue(int x, int y, int v) { + cells[x][y].setValue(v); + } + + public void clearCellValue(int x, int y) { + setCellValue(x, y, 0); + } + + public void setCellModified(int x, int y, boolean modified) { + cells[x][y].setModified(modified); + } + + public void setAllCellsModified(boolean modified) { + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + cells[x][y].setModified(modified); + } + } + } + + public boolean isAnyCellModified() { + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + if (cells[x][y].isModified()) { + return true; + } + } + } + return false; + } + + public void clear() { + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + cells[x][y].setValue(0); + } + } + } + + public int[][] getSquare(int x, int y) { + if (x < 0 || x > 2 || y < 0 || y > 2) { + throw new IllegalArgumentException("Invalid square coordinates"); + } + int[][] square = new int[3][3]; + + for (int u = 0 ; u < 3 ; u++) { + for (int v = 0 ; v < 3 ; v++) { + square[u][v] = getCellValue(3*x+u, 3*y+v); + } + } + + return square; + } + + public int[] getRow(int y) { + if (y < 0 || y > 8) { + throw new IllegalArgumentException("Invalid row number"); + } + int row[] = new int[9]; + + for (int x = 0 ; x < 9 ; x++) { + row[x] = getCellValue(x, y); + } + + return row; + } + + public int[] getColumn(int x) { + if (x < 0 || x > 8) { + throw new IllegalArgumentException("Invalid column number"); + } + int column[] = new int[9]; + + for (int y = 0 ; y < 9 ; y++) { + column[y] = getCellValue(x, y); + } + + return column; + } +} diff -r e70a0e3555fb -r 576e7a2861ae src/main/java/de/uapcore/sudoku/MainMenu.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/sudoku/MainMenu.java Sat Jul 25 14:01:28 2020 +0200 @@ -0,0 +1,98 @@ +/* + * Copyright 2013 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package de.uapcore.sudoku; + +import javax.swing.*; + +/** + * + * @author mike + */ +public class MainMenu { + + private ActionHandler handler; + private JMenuBar menuBar; + + public MainMenu(ActionHandler h) { + handler = h; + menuBar = new JMenuBar(); + menuBar.add(createMenu("Datei", 'd', + createMenuItem("Neu", 'n', "control N", ActionHandler.NEW), + createMenuItem("Öffnen", 'f', "control O", ActionHandler.OPEN), + createMenuItem("Speichern", 's', "control S", ActionHandler.SAVE), + createMenuItem("Speichern als...", 'a', ActionHandler.SAVE_AS), + createSeparator(), + createMenuItem("Prüfen", 'p', "control P", ActionHandler.CHECK), + createMenuItem("Lösen", 'l', "control L", ActionHandler.SOLVE), + createSeparator(), + createMenuItem("Beenden", 'e', ActionHandler.QUIT) + )); + menuBar.add(createMenu("Help", 'h', + createMenuItem("Über...", 'b', "F1", ActionHandler.ABOUT) + )); + } + + private JMenuItem createSeparator() { + // Return null, the createMenu method knows how to handle it + return null; + } + + private JMenu createMenu(String caption, char mnemonic, JMenuItem...items) { + JMenu menu = new JMenu(caption); + menu.setMnemonic(mnemonic); + for (JMenuItem item : items) { + if (item == null) { + menu.addSeparator(); + } else { + menu.add(item); + } + } + return menu; + } + + private JMenuItem createMenuItem(String caption, char mnemonic, + String command) { + return createMenuItem(caption, mnemonic, null, command); + } + + private JMenuItem createMenuItem(String caption, char mnemonic, + String stroke, String command) { + JMenuItem item = new JMenuItem(caption); + item.setMnemonic(mnemonic); + if (stroke != null) { + item.setAccelerator(KeyStroke.getKeyStroke(stroke)); + } + item.setActionCommand(command); + item.addActionListener(handler); + + return item; + } + + public JMenuBar getMenuBar() { + return menuBar; + } +} diff -r e70a0e3555fb -r 576e7a2861ae src/main/java/de/uapcore/sudoku/Solver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/sudoku/Solver.java Sat Jul 25 14:01:28 2020 +0200 @@ -0,0 +1,227 @@ +/* + * Copyright 2013 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package de.uapcore.sudoku; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author mike + */ +public final class Solver { + + public Solver() { + } + + private Integer fillInCandidate(Field f, List[][] candidates, int x, int y) { + Integer c = candidates[x][y].remove(0); + f.setCellValue(x, y, c); + f.setCellModified(x, y, true); + for (int i = 0 ; i < 9 ; i++) { + candidates[x][i].remove(c); + candidates[i][y].remove(c); + } + for (int i = 0 ; i < 3 ; i++) { + for (int j = 0 ; j < 3 ; j++) { + candidates[x-x%3+i][y-y%3+j].remove(c); + } + } + return c; + } + + private void makeBackup(List[][] candidates, List[][] candidatesBackup) { + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + candidatesBackup[x][y] = new ArrayList<>(9); + candidatesBackup[x][y].addAll(candidates[x][y]); + } + } + } + + private void makeBackup(Field f, int[][] fieldBackup) { + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + fieldBackup[x][y] = f.getCellValue(x, y); + } + } + } + + private void restoreBackup(Field f, int[][] fieldBackup) { + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + f.setCellValue(x, y, fieldBackup[x][y]); + } + } + } + + private void restoreBackup(List[][] candidates, List[][] candidatesBackup) { + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + candidates[x][y].clear(); + candidates[x][y].addAll(candidatesBackup[x][y]); + } + } + } + + private boolean solve(Field f, List[][] candidates) { + + // Make backup + List[][] candidatesBackup = new List[9][9]; + int[][] fieldBackup = new int[9][9]; + makeBackup(candidates, candidatesBackup); + makeBackup(f, fieldBackup); + + // Fill in distinct solutions + boolean fillDistinct; + do { + fillDistinct = false; + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + if (f.isCellEmpty(x, y) && candidates[x][y].size() == 1) { + fillInCandidate(f, candidates, x, y); + fillDistinct = true; + } + } + } + } while (fillDistinct); + + // Try out remaining candidates + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + if (f.isCellEmpty(x, y)) { + while (candidates[x][y].size() > 0) { + List[][] cb = new List[9][9]; + makeBackup(candidates, cb); + Integer c = fillInCandidate(f, candidates, x, y); + if (solve(f, candidates)) { + break; + } else { + f.clearCellValue(x, y); + restoreBackup(candidates, cb); + // Remove current candidate anyway + candidates[x][y].remove(c); + } + } + } + if (f.isCellEmpty(x, y)) { + restoreBackup(f, fieldBackup); + restoreBackup(candidates, candidatesBackup); + return false; + } + } + } + + return true; + } + + public boolean solve(Field f) { + + // Calculate initial candidates + List candidates[][] = new List[9][9]; + for (int x = 0 ; x < 9 ; x++) { + for (int y = 0 ; y < 9 ; y++) { + candidates[x][y] = new ArrayList<>(9); + if (f.getCellValue(x, y) == 0) { + // All numbers are candidates + for (int c = 1 ; c <= 9 ; c++) { + candidates[x][y].add(c); + } + // Remove row duplicates + int[] line = f.getRow(y); + for (Integer c : line) { + candidates[x][y].remove(c); + } + // Remove column duplicates + line = f.getColumn(x); + for (Integer c : line) { + candidates[x][y].remove(c); + } + // Remove square duplicates + int[][] square = f.getSquare(x/3, y/3); + for (int[] sq : square) { + for (Integer c : sq) { + candidates[x][y].remove(c); + } + } + } + } + } + + // Backtrack + return solve(f, candidates); + } + + public boolean check(Field f) { + int line[]; + for (int i = 0 ; i < 9 ; i++) { + line = f.getRow(i); + if (!valid(line)) { + return false; + } + line = f.getColumn(i); + if (!valid(line)) { + return false; + } + } + + int square[][]; + for (int x = 0 ; x < 3 ; x++) { + for (int y = 0 ; y < 3 ; y++) { + square = f.getSquare(x, y); + if (!valid(square)) { + return false; + } + } + } + + return true; + } + + private boolean valid(int[] line) { + int numbers[]; + numbers = new int[9]; + for (int i = 0 ; i < 9 ; i++) { + int l = line[i]-1; + if (l >= 0) { + if ((++numbers[l]) > 1) { + return false; + } + } + } + + return true; + } + + private boolean valid(int[][] square) { + int[] line = new int[9]; + for (int x = 0 ; x < 3 ; x++) { + System.arraycopy(square[x], 0, line, 3*x, 3); + } + return valid(line); + } +} diff -r e70a0e3555fb -r 576e7a2861ae src/main/java/de/uapcore/sudoku/Sudoku.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/sudoku/Sudoku.java Sat Jul 25 14:01:28 2020 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright 2013 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package de.uapcore.sudoku; + +import javax.swing.*; +import java.awt.*; + +/** + * + * @author mike + */ +public final class Sudoku extends JFrame { + + public Sudoku() { + super("Sudoku"); + + Field f = new Field(); + ActionHandler h = new ActionHandler(f); + setJMenuBar(new MainMenu(h).getMenuBar()); + + Container content = getContentPane(); + + content.setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(20, 20, 20, 20); + c.fill = GridBagConstraints.HORIZONTAL; + + c.gridx = 0; c.gridy = 0; + content.add(f, c); + c.gridy++; + content.add(new ButtonPanel(h), c); + + pack(); + content.setBackground(Color.WHITE); + setLocationByPlatform(true); + setDefaultCloseOperation(EXIT_ON_CLOSE); + } + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + new Sudoku().setVisible(true); + } +} diff -r e70a0e3555fb -r 576e7a2861ae src/main/java/de/uapcore/sudoku/SudokuTextField.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/sudoku/SudokuTextField.java Sat Jul 25 14:01:28 2020 +0200 @@ -0,0 +1,136 @@ +/* + * Copyright 2013 Mike Becker. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package de.uapcore.sudoku; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +/** + * + * @author mike + */ +public final class SudokuTextField extends JTextField { + + private boolean modified; + + public SudokuTextField() { + setBorder(null); + setBackground(Color.WHITE); + + setFont(new Font("Dialog", Font.PLAIN, 18)); + setHorizontalAlignment(JTextField.CENTER); + + Dimension dim = new Dimension(40,40); + setPreferredSize(dim); + setMinimumSize(dim); + setMaximumSize(dim); + + addKeyListener(new KeyAdapter() { + private void handle(KeyEvent e) { + char c = e.getKeyChar(); + if (!e.isAltDown() && !e.isControlDown() && + !Character.isISOControl(c)) { + // Perform clean input check + if (getText().length() > 0 && getSelectedText() == null) { + if (c != KeyEvent.CHAR_UNDEFINED) { + e.consume(); + } + } else { + if (c < '1' || c > '9') { + e.consume(); + } else { + setModified(true); + } + } + } else { + // We can still be tricked by hotkeys, so do it the hard way + if (!getText().matches("^[1-9]$")) { + setText(""); + } + } + } + + @Override + public void keyPressed(KeyEvent e) { + handle(e); + } + + @Override + public void keyTyped(KeyEvent e) { + handle(e); + } + + @Override + public void keyReleased(KeyEvent e) { + handle(e); + } + + }); + addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + selectAll(); + } + @Override + public void focusLost(FocusEvent e) { + select(0, 0); + } + }); + } + + public int getValue() { + if (getText().length() > 0) { + return Integer.valueOf(getText()); + } else { + return 0; + } + } + + public void setValue(int v) { + if (v == 0) { + setText(""); + } else if (v < 10) { + setText(String.valueOf(v)); + } else { + throw new IllegalArgumentException( + "Sudoku numbers must be in range 0-9 (0 means 'not set')"); + } + } + + public void setModified(boolean modified) { + this.modified = modified; + setForeground(modified?Color.BLUE:Color.BLACK); + } + + public boolean isModified() { + return modified; + } +}