|
|
Java Quellcode |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
package ads; /** * Autoren: Marco May & Torben Brodt */ import fhw.ADSTool; public class SudokuPlay { public static void main(String[] args) { if(args.length < 2) System.out.println("Zur Backtracking Loesung 2 als Paramter uebergeben\nZum intelligenten Loesen eine 3"); ADSTool.resetTime(); if(args.length > 1) for(int i=1; i<args.length; i++) { Sudoku feld = new Sudoku(9, args[0], args[i]); System.out.println(feld); ADSTool.showRTime(); System.out.println(); } } } class Sudoku { // Konstante fuer die Anzahl an Blöcken(9er/12er Sudoku) und die Breite einer Box (3 oder 4) public final int n, box_n; // Zaehlt die Anzahl der erprobten Platzierungen public int tries=0; // Sudoku Feld (noch nicht vorbelegt, da 9^12 moeglich sind) public byte[][] feld; /** * Konstruktor * args[0] = dateiname * args[1] = smart */ public Sudoku(int n, String what, String filename) { this.n = n; this.box_n = (int)Math.sqrt(n); this.feld = new byte[n][n]; //Datei einlesen read(filename); //und loesen if(what.equals("2")) { solveBacktrack(0,0); } else if(what.equals("3")) { smartSolve(); } else if(what.equals("1")) { // belassen fuer einfache Ausgabe } } /** * Liest ein Raetsel ein */ public void read(String filename) { int i=0, j=0; try { int[] file = ADSTool.readIntArray(filename); for(int digit : file) { if(j == n) { j = 0; i++; } feld[i][j] = (byte)digit; j++; } } catch(Exception e) { System.out.println("Bitte mit dem korrekten Dateinamen als Konsolenparameter starten"); System.exit(0); } } /** * Loest das Raetsel durch Backtracking */ public boolean solveBacktrack(int i, int j) { if(j == n) { /* Zeilenende erreicht, * fange wieder links an, * gehe eine Zeile nach unten * oder breche ab, wenn du ganz fertig bist */ i++; if(i == n) return true; j = 0; } if(feld[i][j] > 0) // Das Feld ist erfolgreich gesetzt // Funktionsaufruf mit naechster Spalte return solveBacktrack(i, j+1); for(byte a=1; a<n+1; a++) { if(check(i, j, a) == false) { feld[i][j] = a; if(solveBacktrack(i, j+1)) return true; } } feld[i][j] = 0; // Der Versuch war wohl nichts return false; } /** * Prueft ob dieses Feld gesetzt werden kann (ruft dabei 3 Subfunktionen auf) * @return-> Gibt true zurueck, wenn der Wert bereits existiert */ public boolean check(int i, int j, int value) { tries++; // Fuer die Ausgabe mit SudokuSmart // System.out.println(""+i+"/"+j); if(checkHorizontal(j, value)) return true; if(checkVertical(i, value)) return true; if(checkBox(i, j, value)) return true; return false; } /** * Prueft ob der Wert bereits in der (horizontalen) SPALTE existiert * @return-> Gibt true zurueck, wenn der Wert bereits existiert */ public boolean checkHorizontal(int j, int value) { for(int a=0; a<n; a++) if(feld[a][j] == value) return true; return false; } /** * Prueft ob der Wert bereits in der (vertikalen) REIHE existiert * @return-> Gibt true zurueck, wenn der Wert bereits existiert */ public boolean checkVertical(int i, int value) { for(int a=0; a<n; a++) if(feld[i][a] == value) return true; return false; } /** * Prueft ob der Wert bereits in der BOX existiert * @return-> Gibt true zurueck, wenn der Wert bereits existiert */ public boolean checkBox(int i, int j, int value) { // oberes, linkes Eck der Box herausfinden (2|8 zu 0|6) int i_start = (int)(i/box_n) * box_n; int j_start = (int)(j/box_n) * box_n; for(int a=i_start; a<i_start+box_n; a++) for(int b=j_start; b<j_start+box_n; b++) if(feld[a][b] == value) return true; return false; } /** * SMART: Loest das Raetsel intelligenter */ public boolean smartSolve() { int[] best = smartChoice(); int i=best[0], j=best[1]; if(i == -1 && j == -1) //Das Sudoku funktioniert - es gibt keine leeren Felder mehr return true; for(byte a=1; a<n+1; a++) { // System.out.print("pruefe wert "+a+" in "); if(check(i, j, a) == false) { feld[i][j] = a; if(smartSolve()) return true; } } feld[i][j] = 0; // Der Versuch war wohl nichts return false; } /** * Sucht die Position der besten Möglichkeit * gibt es eine Anzahl von Moeglichkeiten oefter, dann * dann wird die letzte Zahl als beste Moeglichkeit gewaehlt, * um in einem späteren Schleifendurchläufen Performance zu sparen * @return: Gibt die Koordinaten zurueck */ public int[] smartChoice() { int minCount=n, minI=-1, minJ=-1; for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { if(feld[i][j] == 0) { int c = smartCount(i,j); if(c <= minCount) { minCount = c; minI = i; minJ= j; } } } } int[] results = {minI, minJ}; return results; } /** * SMART: Zaehlt die leeren Felder (die Anzahl an moeglichen Loesungen) */ public int smartCount(int i, int j) { boolean[] list = new boolean[10]; for(int a=0; a<n; a++) { if(feld[i][a] > 0) list[feld[i][a]] = true; // Durch "true" schliessen wir die Felder mit Wert aus if(feld[a][j] > 0) list[feld[a][j]] = true; } //Box int i_start = (int)(i/box_n) * box_n; int j_start = (int)(j/box_n) * box_n; for(int a=i_start; a<i_start+box_n; a++) if(a != i) for(int b=j_start; b<j_start+box_n; b++) if(feld[a][b] > 0 && b != j) // >0 schliesst mehr Felder aus, dadurch an Pos1 der Abfrage list[feld[a][b]] = true; // Jetzt zaehlen wir die Felder, die wir gefunden haben byte max=0, possible=0; for(byte a=1; a<=n; a++) { if(list[a] == false) max++; else possible = a; } if(max == 1) { feld[i][j] = possible; } return max; } /** * Ueberschreibt die Ausgabefunktion * Wir verwenden hier nur einen Zaehler * n*n Stringerweiterungungen sind langsamer als StringBuffer */ public String toString() { StringBuffer returns = new StringBuffer(); for(int i=0; i<n*n; i++) { if(i%n == 0 && i > 0) { returns.append("\n"); if(i%(box_n*n) == 0) returns.append("-----------------------\n"); } if (i%box_n == 0 && i%n != 0) returns.append(" |"); returns.append(feld[i/n][i%n] == 0 ? " " : " "+feld[i/n][i%n]); } returns.append("\nEs wurden "+tries+" Platzierungen ausprobiert in "); return returns.toString(); } } |
Quoted
1. Weg: Ein leeres Puzzlefeld wird Zelle für Zelle durch „Auswürfeln“ (Zufallsgenerator) mit Ziffern befüllt. Sobald es zu einem Regelverstoß kommt, muss per Backtracking-Methode eine andere Belegung probiert werden. Dies ist weniger trivial als beim Lösen des Puzzles: Da eine möglichst „zufällige“ Belegung des Puzzlefeldes benötigt wird, kann man nicht einfach alle Ziffern der Reihe nach durchprobieren. Es hindert aber nicht, alle Ziffern, sobald sie einmal „ausgewürfelt“ wurden, als künftig – für die jeweilige Zelle – gesperrt „abzuhaken“ (in einer Tabelle zu markieren)
Quoted
2. Weg: Neun Einsen ohne Regelverstoß im Puzzelfeld verteilen. Dann neun Zweier, neun Dreier, usw. verteilen. Auch hier muss ein Backtracking-Algorithmus angewandt werden.
Quoted
3. Weg: Man füllt eine Zeile oder eine Spalte in beliebiger Reihenfolge mit den erlaubten Ziffern, verschiebt dann mit jeder weiteren Zeile/Spalte die Ziffernfolge, bis man am Schluss alle möglichen Varianten untereinander/nebeneinander in einer n × n-Matrix vorliegen hat. Dies alleine wäre ein äußerst trivial zu lösendes Rätsel, da sich die Ziffernfolgen wiederholen; deswegen sollte man über erlaubte Transformationen diese Matrix nun schrittweise so verändern, dass die Ursprungsziffernfolge sowie die ausgeführten Transformationen nicht mehr nachvollziehbar sind. Erlaubte Transformationen sind z. B. das Invertieren, das Spiegeln (vertikal, horizontal, schräg), Rotieren, Vertauschen ganzer Zeilen oder Spalten, sofern sie innerhalb eines Mini-Quadrates bleiben, oder das Vertauschen ganzer Zeilen und Spalten von Miniquadraten. Etliche dieser Transformationen hintereinander verwischen (fast) alle Hinweise auf die ursprüngliche Ziffernfolge. Von den hier vorgestellten Erstellungsmethoden ist diese die am wenigsten aufwendige aber rechenintensivste.
Dann werde ich das benutzen, enthält ja sowohl Solver als auch Generator.

This post has been edited 4 times, last edit by "hägar" (Jun 10th 2008, 9:27am)