added solving algorithm

Thu, 31 Jan 2013 18:44:44 +0100

author
Mike Becker <universe@uap-core.de>
date
Thu, 31 Jan 2013 18:44:44 +0100
changeset 7
2c0a2766461c
parent 6
5bab2e971333
child 8
e70a0e3555fb

added solving algorithm

easy-sudoku file | annotate | diff | comparison | revisions
src/de/uapcore/sudoku/ActionHandler.java file | annotate | diff | comparison | revisions
src/de/uapcore/sudoku/DocumentHandler.java file | annotate | diff | comparison | revisions
src/de/uapcore/sudoku/Field.java file | annotate | diff | comparison | revisions
src/de/uapcore/sudoku/Solver.java file | annotate | diff | comparison | revisions
test-sudoku file | annotate | diff | comparison | revisions
veryhard-sudoku file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/easy-sudoku	Thu Jan 31 18:44:44 2013 +0100
@@ -0,0 +1,9 @@
+3 _ _ 2 4 _ _ 6 _
+_ 4 _ _ _ _ _ 5 3
+1 8 9 6 3 5 4 _ _
+_ _ _ _ 8 _ 2 _ _
+_ _ 7 4 9 6 8 _ 1
+8 9 3 1 5 _ 6 _ 4
+_ _ 1 9 2 _ 5 _ _
+2 _ _ 3 _ _ 7 4 _
+9 6 _ 5 _ _ 3 _ 2
--- a/src/de/uapcore/sudoku/ActionHandler.java	Sun Jan 27 15:03:57 2013 +0100
+++ b/src/de/uapcore/sudoku/ActionHandler.java	Thu Jan 31 18:44:44 2013 +0100
@@ -134,7 +134,10 @@
     }
     
     private void solve() {
-        // TODO: solve
+        if (!solver.check(field) || !solver.solve(field)) {
+            JOptionPane.showMessageDialog(field, "Das Feld ist nicht lösbar!",
+                    "Sudoku", JOptionPane.WARNING_MESSAGE);
+        }
     }
     
     private boolean saveUnsaved() {
@@ -162,6 +165,7 @@
         switch (e.getActionCommand()) {
         case NEW:
             if (saveUnsaved()) {
+                doc.clearFilename();
                 field.clear();
             }
             break;
--- a/src/de/uapcore/sudoku/DocumentHandler.java	Sun Jan 27 15:03:57 2013 +0100
+++ b/src/de/uapcore/sudoku/DocumentHandler.java	Thu Jan 31 18:44:44 2013 +0100
@@ -76,6 +76,7 @@
                 throw new IOException("Kein Sudoku-Feld enthalten!");
             }
         }
+        field.setAllCellsModified(false);
     }
     
     public void save(Field field) throws IOException {
--- a/src/de/uapcore/sudoku/Field.java	Sun Jan 27 15:03:57 2013 +0100
+++ b/src/de/uapcore/sudoku/Field.java	Thu Jan 31 18:44:44 2013 +0100
@@ -92,6 +92,10 @@
         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();
     }
@@ -100,6 +104,14 @@
         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++) {
--- a/src/de/uapcore/sudoku/Solver.java	Sun Jan 27 15:03:57 2013 +0100
+++ b/src/de/uapcore/sudoku/Solver.java	Thu Jan 31 18:44:44 2013 +0100
@@ -26,6 +26,9 @@
 
 package de.uapcore.sudoku;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  *
  * @author mike
@@ -35,6 +38,144 @@
     public Solver() {
     }
     
+    private Integer fillInCandidate(Field f, List<Integer>[][] 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<Integer>[][] candidates, List<Integer>[][] 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<Integer>[][] candidates, List<Integer>[][] 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<Integer>[][] candidates) {
+        
+        // Make backup
+        List<Integer>[][] 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<Integer>[][] 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<Integer> 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) && complete(f);
+    }
+    
     public boolean check(Field f) {
         int line[];
         for (int i = 0 ; i < 9 ; i++) {
@@ -61,6 +202,25 @@
         return true;
     }
     
+    private boolean complete(Field f) {
+        for (int i = 0 ; i < 9 ; i++) {
+            if (!complete(f.getRow(i))) {
+                return false;
+            }
+            if (!complete(f.getColumn(i))) {
+                return false;
+            }
+        }
+        for (int x = 0 ; x < 3 ; x++) {
+            for (int y = 0 ; y < 3 ; y++) {
+                if (!complete(f.getSquare(x, y))) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+    
     private boolean complete(int[][] square) {
         for (int x = 0 ; x < 3 ; x++) {
             for (int y = 0 ; y < 3 ; y++) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-sudoku	Thu Jan 31 18:44:44 2013 +0100
@@ -0,0 +1,9 @@
+_ 1 _ 9 _ _ 8 _ _
+_ _ _ _ _ 8 _ _ 4
+6 _ 5 _ _ _ 7 _ _
+_ 9 _ _ 6 _ _ _ 8
+_ _ _ 2 _ 7 _ _ _
+8 _ _ _ 3 _ _ 6 _
+_ _ 2 _ _ _ 5 _ 3
+1 _ _ 4 _ _ _ _ _
+_ _ 6 _ _ 2 _ 1 _
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/veryhard-sudoku	Thu Jan 31 18:44:44 2013 +0100
@@ -0,0 +1,9 @@
+_ _ _ 1 9 _ _ _ 8
+1 _ _ 2 _ 7 9 6 _
+6 9 _ _ _ 4 _ _ _
+_ _ _ _ _ _ 1 _ _
+_ 5 _ 6 _ 9 _ 7 _
+_ _ 3 _ _ _ _ _ _
+_ _ _ 9 _ _ _ 8 1
+_ 1 6 7 _ 8 _ _ 3
+9 _ _ _ 1 2 _ _ _

mercurial