PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Sudoku-Löser


josefYY
2006-04-06, 12:39:04
Hallo Miteinander

Also ich hab in Java ein kleines Programm geschrieben das diese Zahlenrätsel 'Sudoko' löst.
Würde das gerne mit samt Quellcode irgendwo uploaden.
Weiss aber nur nicht wo.
Was sehr erwünscht wäre, wären Anmerkungen, Kommentare, Kritiken zum Programmierstil oder sonst irgend was. Ebenso erwünscht wären zusätzliche Test's :wink: . Ich habs bisher mit einer Handvoll von Rätseln aus der Online-Ausgabe der Zeit getestet. Die wurden allesamt gelöst. Es könnte aber schwerere Rätsel geben, für die ich ausgefeiltere Strategien programmierenn muss.
Eine Anmerkung noch: Das Programm ist derzeit nicht so komfortabel. Sprich es funktioniert nur über die Kommandozeile. Ich habe mich bisher nur auf das lösen der Rätsel konzentriert. Die Start-Konstellation muss direkt in den Java-Code eingegeben und anschliessend compiliert werden. Due Ausgabe der Lösung erfolgt über System.out's.
Ich werde womöglich später mal ein Applet daraus machen. Oder vielleicht hat ja jemand Lust das zu tun :smile: Hab schon lange nicht mehr in Swing programmiert und auch nicht so die grosse Lust dazu.

P.S. Ich kann den Code auch direkt hier rein schreiben wenn's nicht stört?
Wären knapp 700 Zeilen.

Tidus
2006-04-06, 12:42:08
wenn du den code hier schreibe´n willst dann aber mit den tags ;)

Paran
2006-04-06, 12:51:19
zum Testen, also hier ist ein Generator, welchen einen Logarythmus ausnutzt

http://www.opensky.ca/~jdhildeb/software/sudokugen/

josefYY
2006-04-06, 13:23:04
zum Testen, also hier ist ein Generator, welchen einen Logarythmus ausnutzt

http://www.opensky.ca/~jdhildeb/software/sudokugen/
Danke für den Link. Aber das Problem ist nicht Rätsel zu finden, sondern sie auch zu testen :wink:, weils derzeit halt nicht so komfortabel ist.
Nun gut hier der Code:
Im Code sind noch jede Menge Zeilen die ich zum debuggen verwendet habe. Auf Performance habe ich bisher auch noch nicht so geachtet.
Verbesserungsvorschläge in jedweder Beziehung sind ausdrücklich erwünscht!!
Danke!

import java.util.HashMap;
import java.util.Map;

public class Sudoku {
static int intArray[][] = new int[9][9];
static int startArray[][] = new int[9][9];

public static void main(String[] args) {
initialisiereStartaufstellung();
ausgabeStartArray();

while (raetselNichtGeloest()) {
hPLinienStrategie();
vPLinienStrategie();
streicheZifferStrategie();
}
ausgabeFinalArray();
}

// Horizontale-Parallele-Linien-Strategie
public static void hPLinienStrategie() {
//System.out.println("Horizontale-Parallele-Linien-Strategie");
// for (int ziffer = 1; ziffer <= 9; ziffer++) {
for (int ziffer = 1; ziffer <= 9; ziffer++) {
//System.out.println("Ziffer " + ziffer + " wird nun untersucht.");
// Horizontale parallele Linien pro '3-Linien-Block' prüfen
for (int j = 0; j <= 2; j++) {
// for (int j = 1; j <= 1; j++) {
// Parallele-Linien-Strategie:
// Es wird geprüft ob die Ziffer in diesem 'Linien-Block' in genau 2 9er-
// Blöcken vorhanden ist. Nur dann kann eventuell auch die Position im
// fehlenden 9-er-Block identifiziert werden.
int anzahlTreffer = 0;
// Zeilennummer in der die Ziffer stehen muss, in dem 9er-Block in dem die
// Ziffer noch fehlt. Die Herleitung ist ein ein bisschen 'tricky'.
// Initialwert ist 6 (Summe von 1, 2 und 3). Immer wenn die Position in
// einem der 3 9er-Blöcke identifiziert wurde, wird diese von 'freieZeile'
// subtrahiert.
int freieZeile = 6;
// Nummer des Blocks in dem die Ziffer noch fehlt. Analog wie 'freieZeile'.
int freierBlock = 6;

boolean linksTreffer = false;
boolean mitteTreffer = false;
boolean rechtsTreffer = false;

// Prüfe linken 9er-Block
int positionLinkerBlock = pruefe_9er_Block(ziffer, 0, j);
if (positionLinkerBlock != 0) {
//System.out.println("positionLinkerBlock: " + positionLinkerBlock);
//System.out.println("links treffer");
anzahlTreffer++;
linksTreffer = true;
freierBlock = freierBlock - 1;
if (positionLinkerBlock >= 7) {
freieZeile = freieZeile - 3;
}
else if (positionLinkerBlock >= 4) {
freieZeile = freieZeile - 2;
}
else {
freieZeile = freieZeile - 1;
}
}
// Prüfe mittleren 9er-Block
int positionMittlererBlock = pruefe_9er_Block(ziffer, 1, j);
if (positionMittlererBlock != 0) {
//System.out.println("positionMittlererBlock: " + positionMittlererBlock);
//System.out.println("mitte treffer");
anzahlTreffer++;
mitteTreffer = true;
freierBlock = freierBlock - 2;
if (positionMittlererBlock >= 7) {
freieZeile = freieZeile - 3;
}
else if (positionMittlererBlock >= 4) {
freieZeile = freieZeile - 2;
}
else {
freieZeile = freieZeile - 1;
}
}
// Prüfe rechten 9er-Block
int positionRechterBlock = pruefe_9er_Block(ziffer, 2, j);
if (positionRechterBlock != 0) {
//System.out.println("positionRechterBlock: " + positionRechterBlock);
//System.out.println("rechts treffer");
anzahlTreffer++;
rechtsTreffer = true;
freierBlock = freierBlock - 3;
if (positionRechterBlock >= 7) {
freieZeile = freieZeile - 3;
}
else if (positionRechterBlock >= 4) {
freieZeile = freieZeile - 2;
}
else {
freieZeile = freieZeile - 1;
}
}

// Nun muss noch geprüft werden ob in der Zeile mit der Nummer 'freieZeile'
// des 9er-Blocks in dem die Ziffer in jedem Fall stehen muss, noch genau ein
// Feld frei ist. Falls ja hat man die Position der Ziffer eindeutig
// identfiziert und man ist fertig.
if (anzahlTreffer == 2) {
//System.out.println("2 Treffer gefunden");
// x = freierBlock
// y = j
int anzahlFreiePositionen = 0;
int freieStelle_1 = 0;
int freieStelle_2 = 0;
int freieStelle_3 = 0;
for (int m = 0; m <= 2; m++){
/*
System.out.println("Ziffer: " + ziffer);
System.out.print("x: ");
System.out.println(3*(freierBlock-1));
System.out.print("y: j=" + j + " freieZeile=" + freieZeile);
System.out.println(3*j+(freieZeile-1));
*/
int x = 3*(freierBlock-1)+m;
int y = 3*j+(freieZeile-1);
//System.out.println("x:" + x);
//System.out.println("y: " + y + "| j: " + j + " freieZeile: " + freieZeile);
if (intArray[3*(freierBlock-1)+m][3*j+(freieZeile-1)] == 0) {
anzahlFreiePositionen++;
if (anzahlFreiePositionen == 1) {
freieStelle_1 = m;
}
else if (anzahlFreiePositionen == 2) {
freieStelle_2 = m;
}
if (anzahlFreiePositionen == 3) {
freieStelle_3 = m;
}
}
}
//System.out.println("anzahlFreiePositionen: " + anzahlFreiePositionen);
// Jippie, ein Leerfeld weniger.
if (anzahlFreiePositionen == 1) {
//System.out.println("HALLO");
intArray[3*(freierBlock-1)+freieStelle_1][3*j+(freieZeile-1)] = ziffer;
}
else if (anzahlFreiePositionen == 2) {
//System.out.println("2 freie Positionen.");
// Die Position ist zwar nicht eindeutig. Aber wenn wir in einer der
// Vertikalen zu einem der offenen Positionen die Ziffer wiederfinden, könnte
// man diese Position somit ausschliessen und wir haetten die finale Position
// eindeutig identifiziert. siehe Skizze:
// 1 |2 | 8
// | 1| 3
// | 6 | 5X <---
// ------------
// 1
// ...
// D.h. die Ziffer wurde in der Spalte freieStelle_1 gefunden. Somit muss die
// Ziffer an der anderen Position (freieStelle_2) stehen.
if (pruefe_Spalte_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_1)) {
intArray[3*(freierBlock-1)+freieStelle_2][3*j+(freieZeile-1)] = ziffer;
}
else if (pruefe_Spalte_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_2)) {
intArray[3*(freierBlock-1)+freieStelle_1][3*j+(freieZeile-1)] = ziffer;
}
}
else if (anzahlFreiePositionen == 3) {
// Die Position ist zwar nicht eindeutig. Aber wenn wir in 2 der 3
// vertikalen an den offenen Positionen die Ziffer wiederfinden, könnte
// man diese Positionen somit ausschliessen und wir haetten die finale Position
// eindeutig identifiziert. siehe Skizze:
// 1 |2 | 8
// | 1| 3
// | 6 | X <---
// ------------
// 1
//
//
// ------------
// 1
// ...
// In Spalten freieStelle_1 und freieStelle_2 ist Ziffer gefunden worden.
// D.h. die Ziffer steht an der Position freieStelle_3
if (pruefe_Spalte_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_1) &&
pruefe_Spalte_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_2)) {
intArray[3*(freierBlock-1)+freieStelle_3][3*j+(freieZeile-1)] = ziffer;
}
// In Spalten freieStelle_1 und freieStelle_3 ist Ziffer gefunden worden.
// D.h. die Ziffer steht an der Position freieStelle_2
else if (pruefe_Spalte_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_1) &&
pruefe_Spalte_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_3)) {
intArray[3*(freierBlock-1)+freieStelle_2][3*j+(freieZeile-1)] = ziffer;
}
// In Spalten freieStelle_2 und freieStelle_3 ist Ziffer gefunden worden.
// D.h. die Ziffer steht an der Position freieStelle_1
else if (pruefe_Spalte_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_2) &&
pruefe_Spalte_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_3)) {
intArray[3*(freierBlock-1)+freieStelle_1][3*j+(freieZeile-1)] = ziffer;
}
}
} // if (anzahlTreffer == 2) {
} // for (int j = 0; j <= 2; j++) {
} // for (int ziffer = 0; ziffer < 9; ziffer++) {
} // public static void hPLinienStrategie() {


// Vertikale-Parallele-Linien-Strategie
public static void vPLinienStrategie() {
//System.out.println("Vertikale-Parallele-Linien-Strategie");
// for (int ziffer = 1; ziffer <= 9; ziffer++) {
for (int ziffer = 1; ziffer <= 9; ziffer++) {
//System.out.println("Ziffer " + ziffer + " wird nun untersucht.");
// Vertikale parallele Linien pro '3-Linien-Block' prüfen
for (int vBlockIndex = 0; vBlockIndex <= 2; vBlockIndex++) {
// for (int vBlockIndex = 1; vBlockIndex <= 1; vBlockIndex++) {
// Vertikale-Parallele-Linien-Strategie:
// Es wird geprüft ob die Ziffer in diesem 'Spalten-Block' in genau 2 9er-
// Blöcken vorhanden ist. Nur dann kann eventuell auch die Position im
// fehlenden 9-er-Block identifiziert werden.
int anzahlTreffer = 0;
// Spaltennummer in der die Ziffer stehen muss, in dem 9er-Block in dem die
// Ziffer noch fehlt. Die Herleitung ist ein ein bisschen 'tricky'.
// Initialwert ist 6 (Summe von 1, 2 und 3). Immer wenn die Position in
// einem der 3 9er-Blöcke identifiziert wurde, wird diese von 'freieZeile'
// subtrahiert.
int freieSpalte = 6;
// Nummer des Blocks in dem die Ziffer noch fehlt. Analog wie 'freieZeile'.
int freierBlock = 6;

boolean obenTreffer = false;
boolean mitteTreffer = false;
boolean untenTreffer = false;

// Prüfe oberer 9er-Block
int positionObererBlock = pruefe_9er_Block(ziffer, vBlockIndex, 0);
if (positionObererBlock != 0) {
//System.out.println("positionObererBlock: " + positionObererBlock);
//System.out.println("oben treffer");
anzahlTreffer++;
obenTreffer = true;
freierBlock = freierBlock - 1;
if (positionObererBlock == 3 ||
positionObererBlock == 6 ||
positionObererBlock == 9) {
freieSpalte = freieSpalte - 3;
}
else if (positionObererBlock == 2 ||
positionObererBlock == 5 ||
positionObererBlock == 8) {
freieSpalte = freieSpalte - 2;
}
else {
freieSpalte = freieSpalte - 1;
}
//System.out.println("freieSpalte: " + freieSpalte);
}
// Prüfe mittleren 9er-Block
int positionMittlererBlock = pruefe_9er_Block(ziffer, vBlockIndex, 1);
if (positionMittlererBlock != 0) {
//System.out.println("positionMittlererBlock: " + positionMittlererBlock);
//System.out.println("mitte treffer");
anzahlTreffer++;
mitteTreffer = true;
freierBlock = freierBlock - 2;
if (positionMittlererBlock == 3 ||
positionMittlererBlock == 6 ||
positionMittlererBlock == 9) {
freieSpalte = freieSpalte - 3;
}
else if (positionMittlererBlock == 2 ||
positionMittlererBlock == 5 ||
positionMittlererBlock == 8) {
freieSpalte = freieSpalte - 2;
}
else {
freieSpalte = freieSpalte - 1;
}
//System.out.println("freieSpalte: " + freieSpalte);
}
// Prüfe unterer 9er-Block
int positionUntererBlock = pruefe_9er_Block(ziffer, vBlockIndex, 2);
if (positionUntererBlock != 0) {
//System.out.println("positionUntererBlock: " + positionUntererBlock);
//System.out.println("unten treffer");
anzahlTreffer++;
untenTreffer = true;
freierBlock = freierBlock - 3;
if (positionUntererBlock == 3 ||
positionUntererBlock == 6 ||
positionUntererBlock == 9) {
freieSpalte = freieSpalte - 3;
}
else if (positionUntererBlock == 2 ||
positionUntererBlock == 5 ||
positionUntererBlock == 8) {
freieSpalte = freieSpalte - 2;
}
else {
freieSpalte = freieSpalte - 1;
}
//System.out.println("freieSpalte: " + freieSpalte);
}

// Nun muss noch geprüft werden ob in der Spalte mit der Nummer 'freieZeile'
// des 9er-Blocks in dem die Ziffer in jedem Fall stehen muss, noch genau ein
// Feld frei ist. Falls ja hat man die Position der Ziffer eindeutig
// identfiziert und man ist fertig.
if (anzahlTreffer == 2) {
//System.out.println("2 Treffer gefunden");
// x = freierBlock
// y = j
int anzahlFreiePositionen = 0;
int freieStelle_1 = 0;
int freieStelle_2 = 0;
int freieStelle_3 = 0;
for (int m = 0; m <= 2; m++){
/*
System.out.println("Ziffer: " + ziffer);
System.out.print("x: ");
System.out.println(3*(freierBlock-1));
System.out.print("y: j=" + j + " freieSpalte=" + freieSpalte);
System.out.println(3*j+(freieSpalte-1));
*/

int x = 3*vBlockIndex+(freieSpalte-1);
int y = 3*(freierBlock-1)+m;
//System.out.println("x:" + x + "| vBlockIndex: " + vBlockIndex + " freieSpalte: " + freieSpalte);
//System.out.println("y: " + y);
if (intArray[3*(vBlockIndex)+(freieSpalte-1)][3*(freierBlock-1)+m] == 0) {
anzahlFreiePositionen++;
if (anzahlFreiePositionen == 1) {
freieStelle_1 = m;
}
else if (anzahlFreiePositionen == 2) {
freieStelle_2 = m;
}
if (anzahlFreiePositionen == 3) {
freieStelle_3 = m;
}
}
}

//System.out.println("anzahlFreiePositionen: " + anzahlFreiePositionen);
// Jippie, ein Leerfeld weniger.
if (anzahlFreiePositionen == 1) {
//System.out.println("HALLO");
intArray[3*(vBlockIndex)+(freieSpalte-1)][3*(freierBlock-1)+freieStelle_1] = ziffer;
}
else if (anzahlFreiePositionen == 2) {
//System.out.println("2 freie Positionen.");
// Die Position ist zwar nicht eindeutig. Aber wenn wir in einer der
// Horizontalen zu einem der offenen Positionen die Ziffer wiederfinden, könnte
// man diese Position somit ausschliessen und wir hätten die finale Position
// eindeutig identifiziert. siehe Skizze:
// 1 | |
// | |
// | |
//------------
// 2 | |
// 6| |
// 1 | |
//------------
// 3X| |
// 8 5| |
// |1 |
// D.h. die Ziffer wurde in der Zeile freieStelle_1 gefunden. Somit muss die
// Ziffer an der anderen Position (freieStelle_2) stehen.
if (pruefe_Zeile_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_1)) {
intArray[3*(vBlockIndex)+(freieSpalte-1)][3*(freierBlock-1)+freieStelle_2] = ziffer;
}
else if (pruefe_Zeile_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_2)) {
intArray[3*(vBlockIndex)+(freieSpalte-1)][3*(freierBlock-1)+freieStelle_1] = ziffer;
}
}
else if (anzahlFreiePositionen == 3) {
// Die Position ist zwar nicht eindeutig. Aber wenn wir in 2 der 3
// vertikalen an den offenen Positionen die Ziffer wiederfinden, könnte
// man diese Positionen somit ausschliessen und wir haetten die finale Position
// eindeutig identifiziert. siehe Skizze:
// 1 | |
// | |
// | |
//------------
// 2 | |
// 6| |
// 1 | |
//------------
// 3X| |
// 8 | |1
// |1 |
// In den Zeilen freieStelle_1 und freieStelle_2 ist Ziffer gefunden worden.
// D.h. die Ziffer steht an der Position freieStelle_3
if (pruefe_Zeile_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_1) &&
pruefe_Zeile_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_2)) {
intArray[3*(vBlockIndex)+(freieSpalte-1)][3*(freierBlock-1)+freieStelle_3] = ziffer;
}
// In den Zeilen Spalten freieStelle_1 und freieStelle_3 ist Ziffer gefunden worden.
// D.h. die Ziffer steht an der Position freieStelle_2
else if (pruefe_Zeile_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_1) &&
pruefe_Zeile_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_3)) {
intArray[3*(vBlockIndex)+(freieSpalte-1)][3*(freierBlock-1)+freieStelle_2] = ziffer;
}
// In den Zeilen freieStelle_2 und freieStelle_3 ist Ziffer gefunden worden.
// D.h. die Ziffer steht an der Position freieStelle_1
else if (pruefe_Zeile_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_2) &&
pruefe_Zeile_Auf_Ziffer(ziffer, 3*(freierBlock-1)+freieStelle_3)) {
intArray[3*(vBlockIndex)+(freieSpalte-1)][3*(freierBlock-1)+freieStelle_1] = ziffer;
}
}
} // if (anzahlTreffer == 2) {
} // for (int j = 0; j <= 2; j++) {
} // for (int ziffer = 0; ziffer < 9; ziffer++) {
} // public static void vPLinienStrategie() {


//
public static void streicheZifferStrategie() {
HashMap restZiffern = new HashMap();
for (int x = 0; x <= 8; x++) {
for (int y = 0; y <= 8; y++) {
// Für alle leere Stellen
if (intArray[x][y] == 0) {
//System.out.println("Stelle x:=" + x + " y:=" + y + " ist leer");
restZiffern.clear();
// Initialisierung der Liste
for (int i = 1; i <= 9; i++) {
restZiffern.put(i+"", i+"");
}

// Es werden alle Ziffern die es in dieser Zeile bereits gibt gestrichen.
HashMap strichliste = getZeilenZiffern(y);
for (int j = 1; j <= 9 ; j++) {
if (strichliste.containsValue(j+"")) {
restZiffern.remove(j+"");
}
}
// Es werden alle Ziffern die es in dieser Spalte bereits gibt gestrichen.
strichliste = getSpaltenZiffern(x);
for (int j = 1; j <= 9 ; j++) {
if (strichliste.containsValue(j+"")) {
restZiffern.remove(j+"");
}
}
// Es werden alle Ziffern die es in diesem Block bereits gibt gestrichen.
strichliste = getBlockZiffern(x, y);
for (int j = 0; j <= 9 ; j++) {
if (strichliste.containsValue(j+"")) {
restZiffern.remove(j+"");
}
}
//System.out.println(( (String) restZiffern.entrySet().iterator().next()+"" ).substring(0,1) );
//System.out.println("restZiffern.size() := " + restZiffern.size());
if (restZiffern.size() == 1) {
//System.out.println("Restziffern.sitz");
// intArray[x][y] = Integer.parseInt(((String) restZiffern.entrySet().iterator().next()).substring(0,1));
Map.Entry entry = (Map.Entry) restZiffern.entrySet().iterator().next();
//System.out.println("Map.Entry:=" + entry.getValue());
intArray[x][y] = Integer.parseInt((String) entry.getValue());
// ((Map.Entry) restZiffern.entrySet().iterator().next()).
}
} // if (intArray[x][y] == 0) {
} // for (int y = 0; y <= 8; y++) {
} // for (int x = 0; x <= 8; x++) {
} // public static void streicheZifferStrategie() {


// Gibt alle Ziffern in einer Zeile in Form einer Liste zurück.
public static HashMap getZeilenZiffern(int y) {
HashMap zeilenZiffern = new HashMap();
for (int x = 0; x <= 8; x++) {
if (intArray[x][y] != 0) {
zeilenZiffern.put(intArray[x][y]+"", intArray[x][y]+"");
}
}
return zeilenZiffern;
}

// Gibt alle Ziffern in einer Spalte in Form einer Liste zurück.
public static HashMap getSpaltenZiffern(int x) {
HashMap spaltenZiffern = new HashMap();
for (int y = 0; y <= 8; y++) {
if (intArray[x][y] != 0) {
spaltenZiffern.put(intArray[x][y]+"", intArray[x][y]+"");
}
}
return spaltenZiffern;
}


// Gibt alle Ziffern in einem 9er-Block in Form einer Liste zurück.
public static HashMap getBlockZiffern(int x, int y) {
HashMap blockZiffern = new HashMap();
int m;
int n;

if (x<=2) {
m = 0;
}
else if (x<=5) {
m = 3;
}
else {
m = 6;
}

if (y<=2) {
n = 0;
}
else if (y<=5) {
n = 3;
}
else {
n = 6;
}

for (int xindex = 0+m; xindex <= 2+m; xindex++) {
for (int yindex = 0+n; yindex <= 2+n; yindex++) {
if (intArray[xindex][yindex] != 0) {
blockZiffern.put(intArray[xindex][yindex]+"", intArray[xindex][yindex] + "");
}
}
}
return blockZiffern;
}


// Diese Methode prüft ob in einer bestimmten Zeile eine Ziffer vorhanden ist.
public static boolean pruefe_Zeile_Auf_Ziffer(int ziffer, int y) {
boolean zifferGefunden = false;
for (int m = 0; m <= 8; m++) {
if (intArray[m][y] == ziffer) {
zifferGefunden = true;
break;
}
}
return zifferGefunden;
}


// Diese Methode prüft ob in einer bestimmten Spalte eine Ziffer vorhanden ist.
public static boolean pruefe_Spalte_Auf_Ziffer(int ziffer, int x) {
boolean zifferGefunden = false;
for (int m = 0; m <= 8; m++) {
if (intArray[x][m] == ziffer) {
zifferGefunden = true;
break;
}
}
return zifferGefunden;
}


/* Prüft ob eine Ziffer in einer durch die Indizes x und y identifiziertem 9er-Block
* vorhanden ist. siehe Skizze
* x-Achse
* y- 0,0 1,0 2,0
* Ach 0,1 1,1 2,1
* se 0,2 1,2 2,2
* Das zurückgelieferte Ergebnis ist ein Integer zwischen 0 und 9. 0 bedeutet
* die Ziffer ist in dem Block nicht vorhanden. Alle anderen Zahlen entsprechenden
* nachfolgenden Positionen im 9er-Block.
* 1 2 3
* 4 5 6
* 7 8 9
*/
public static int pruefe_9er_Block(int ziffer, int x, int y) {
//System.out.println("Prufe 9er Block mit Ziffer: " + ziffer + "| x: " + x + "| y: " +y);
int ergebnis = 0;
for (int m = 0; m <= 2; m++) {
for (int n = 0; n <= 2; n++) {
int a = 3*x+m;
int b = 3*y+n;
//System.out.println("x:= " + a + " y: " + b);
if (intArray[3*x+m][3*y+n] == ziffer) {
//System.out.println("m: " + m + "| n: " + n);
return m+3*n+1;
}

}
}
return ergebnis;
}

// Prüfung ob bereits alle Felder gefüllt sind.
public static boolean raetselNichtGeloest() {
boolean nichtFertig = false;
for (int i = 0; i <= 8; i++) {
for (int j = 0; j <= 8; j++) {
if (intArray[i][j] == 0) {
nichtFertig = true;
}
}
}
return nichtFertig;
}

// Ausgabe Start-Array
private static void ausgabeStartArray() {
System.out.println("-------S-t-a-r-t-------");
System.out.println("-----------------------");
for (int y = 0; y <= 8; y++) {
System.out.print("| ");
for (int x = 0; x <= 8; x++) {
System.out.print(startArray[x][y] + " ");
if (x%3 == 2) {
System.out.print("|");
}
}
System.out.println("");
if (y%3 == 2) {
System.out.println("-----------------------");
}
}
}

// Ausgabe
private static void ausgabeFinalArray() {
System.out.println("-------F-i-n-a-l-------");
System.out.println("-----------------------");
for (int y = 0; y <= 8; y++) {
System.out.print("| ");
for (int x = 0; x <= 8; x++) {
System.out.print(intArray[x][y] + " ");
if (x%3 == 2) {
System.out.print("|");
}
}
System.out.println("");
if (y%3 == 2) {
System.out.println("-----------------------");
}
}
}

// Initialisierung der Startziffern.
public static void initialisiereStartaufstellung() {
/*
// http://sudoku.zeit.de/sudoku/kunden/die_zeit/ vom 30.03.2006 leicht
//Zeile 1
intArray[1][0] = 9; intArray[3][0] = 8; intArray[5][0] = 1;
intArray[6][0] = 2; intArray[8][0] = 5;
// Zeile 2
intArray[0][1] = 7; intArray[1][1] = 3; intArray[5][1] = 5;
intArray[8][1] = 4;
// Zeile 3
intArray[6][2] = 9; intArray[7][2] = 1;
// Zeile 4
intArray[0][3] = 5; intArray[3][3] = 2;
// Zeile 5
intArray[1][4] = 8; intArray[2][4] = 2; intArray[4][4] = 9;
intArray[7][4] = 5; intArray[8][4] = 3;
// Zeile 6
intArray[1][5] = 7; intArray[4][5] = 1; intArray[8][5] = 6;
// Zeile 7
intArray[0][6] = 9; intArray[2][6] = 3; intArray[3][6] = 1;
intArray[5][6] = 2; intArray[7][6] = 7;
// Zeile 8
intArray[0][7] = 6; intArray[1][7] = 1; intArray[5][7] = 9;
// Zeile 9
intArray[1][8] = 5; intArray[3][8] = 4; intArray[4][8] = 3;
*/
// http://sudoku.zeit.de/sudoku/kunden/die_zeit/ vom 04.04.2006 leicht
//Zeile 1
intArray[5][0] = 1; intArray[8][0] = 4;
// Zeile 2
intArray[3][1] = 6; intArray[6][1] = 2; intArray[8][1] = 3;
// Zeile 3
intArray[3][2] = 4; intArray[4][2] = 8; intArray[7][2] = 7;
// Zeile 4
intArray[2][3] = 6; intArray[3][3] = 5;
// Zeile 5
intArray[0][4] = 2; intArray[2][4] = 9; intArray[4][4] = 4;
intArray[5][4] = 8; intArray[8][4] = 7;
// Zeile 6
intArray[0][5] = 5; intArray[3][5] = 3; intArray[5][5] = 9;
// Zeile 7
intArray[1][6] = 7; intArray[2][6] = 3; intArray[4][6] = 5;
intArray[6][6] = 1; intArray[7][6] = 9; intArray[8][6] = 8;
// Zeile 8
intArray[1][7] = 5; intArray[3][7] = 7; intArray[8][7] = 2;
// Zeile 9
intArray[0][8] = 1; intArray[1][8] = 4; intArray[4][8] = 9;
intArray[6][8] = 7; intArray[7][8] = 5;

for (int a = 0; a <= 8; a++) {
for (int b = 0; b <= 8; b++) {
startArray[a][b] = intArray[a][b];
}
}
}
}

Monger
2006-04-06, 14:09:29
Der Code scheint mir erstaunlich... linear zu sein.

Ich hab jetzt leider keine Zeit ihn ausführlich durchzuschauen, aber mir fällt auf, dass du extrem viel mit statischen Methoden arbeitest. Das muss nicht schlecht sein, aber ein wenig überrascht es mich doch. Es funktioniert ja offensichtlich, aber um dir die Wartung und Erweiterung im nachhinein zu erleichtern, würde ich an deiner Stelle ein bißchen Energie in das Refaktoring stecken. Du könntest z.B. eine Klasse machen, in der du deine ganzen Strategien sammelst. Wenn du davon abstrahierst, kannst du dein eigentliches Programm relativ schnell mit neuen Taktiken füttern oder alte rausschmeißen.

Es gibt halt auch noch andere Kriterien für guten Code als nur "es geht". Und nachdem du es ja offenbar jetzt hingekriegt hast, könntest du jetzt versuchen es richtig gut zu machen! :)

Trap
2006-04-06, 14:12:40
Ich hab schon Lösungen in 30 Zeilen gesehen die beim ersten Durchlesen direkt verständlich sind (auch ohne Kommentar).

josefYY
2006-04-06, 14:26:03
Der Code scheint mir erstaunlich... linear zu sein.
Ich vermute du sprichst darauf an das mein Code trotz Java nicht sonderlich OO wirkt. Das kommt wahrscheinlich daher das ich mit prozeduralen Programmiersprachen angefangen habe. :wink:


Ich hab jetzt leider keine Zeit ihn ausführlich durchzuschauen, aber mir fällt auf, dass du extrem viel mit statischen Methoden arbeitest. Das muss nicht schlecht sein, aber ein wenig überrascht es mich doch. Es funktioniert ja offensichtlich, aber um dir die Wartung und Erweiterung im nachhinein zu erleichtern, würde ich an deiner Stelle ein bißchen Energie in das Refaktoring stecken. Du könntest z.B. eine Klasse machen, in der du deine ganzen Strategien sammelst. Wenn du davon abstrahierst, kannst du dein eigentliches Programm relativ schnell mit neuen Taktiken füttern oder alte rausschmeißen.

Es gibt halt auch noch andere Kriterien für guten Code als nur "es geht". Und nachdem du es ja offenbar jetzt hingekriegt hast, könntest du jetzt versuchen es richtig gut zu machen! :)

Das mit den statischen Methoden ist nur weil die main-Methode statisch ist, und ich somit auch nur statische-Methoden aufrufen kann.
Die Methoden würde ich normalerweise sonst nicht als statisch deklarieren. Mit dem Refaktoring hast du natürlich recht. Hab im ersten Anlauf nur versucht das Ding zum laufen zu bekommen, und auf derlei Dinge noch nicht geachtet. Danke für den Kommentar!!

Monger
2006-04-06, 14:37:37
Ich vermute du sprichst darauf an das mein Code trotz Java nicht sonderlich OO wirkt. Das kommt wahrscheinlich daher das ich mit prozeduralen Programmiersprachen angefangen habe. :wink:

Iiihhh! :D

Denk mal aber darüber nach, ob du irgendwas sinnvoll zusammenfassen kannst. Du hast z.B. sehr viele Methoden, die über einen bestimmten Bereich (meistens eine Zeile) irgendwas checken. Die unterscheiden sich codemäßig teilweise sehr wenig. Wenn du das sinnvoll trennst und generalisierst, kannst du vermutlich viel Code Redundanz beseitigen. Und merke: je weniger Codezeilen, desto schneller geht die Fehlersuche! ;)

In deinem Fall machst es wohl nicht so viel Sinn, aber im allgemeinen würde ich dir vorschlagen, dein Spielfeld als Objekt zu modellieren, und mit entsprechenden Zugriffsmethoden auszustatten. Wenn du das richtig drehst, kannst du dann das selbe Objekt zum Aufbau deiner grafischen Oberfläche verwenden.

Merke: alles was sich wie ein Objekt anfühlt, sollte auch ein Objekt sein! ;)

SamStone
2006-04-06, 15:08:08
Ich habs mir jetzt nicht so genau angeguckt, aber geh ich recht mit der Annahme, dass das Programm einfach nacheinander überall da die passenden Zahlen einsetzt, wo es garkeine andere Möglichkeit gibt, und dann wieder von vorne?

Wir hatten das letztens mal in Info besprochen, und da haben wir den gerade von mir beschriebenen Ansatz + Backtracking benutzt wenn ich mich richtig erinnere.

josefYY
2006-04-06, 15:13:58
Ich hab schon Lösungen in 30 Zeilen gesehen die beim ersten Durchlesen direkt verständlich sind (auch ohne Kommentar).
Das mit den 30 Zeilen meinst du doch nicht ernst, oder?
Ich hatte bisher noch nie gegoogelt ob es Sudoku-Löser bereits gibt. Sondern einfach so drauf los entwickelt, weil ich dachte es wäre eine interessante Aufgabe. Musste gerade eben aber feststellen das es Sudoku-Löser "wie Sand Meer" gibt. :biggrin:
Die Solver die ich im Web so gefunden habe, gehen meist mit einer anderen Strategie an das Problem heran als ich es tue. Deren Strategien sind sicherlich einfacher zu verstehen und auch zu implementieren. Aber als Mensch würde man die Rätsel so nie lösen. Wäre viel zu aufwendig.
Ich wollte jedoch das Problem auf die selbe Art lösen wie ich es selbst (als Mensch) auch tue. Das fande ich weitaus interessanter und auch anspruchsvoller.

josefYY
2006-04-06, 15:51:39
Ich habs mir jetzt nicht so genau angeguckt, aber geh ich recht mit der Annahme, dass das Programm einfach nacheinander überall da die passenden Zahlen einsetzt, wo es garkeine andere Möglichkeit gibt, und dann wieder von vorne?

Nicht ganz. Der von dir genannte Ansatz ist nur eine von 3 Strategien die ich verwende. Bin mir nicht ganz sicher ob diese alleine ausreichen würde.
Falls diese alleine reichen würde, könnte man den Code wahrscheinlich auf ein zehntel zusammenstampfen. :biggrin:
Eine weitere meiner Strategieen die ich jetzt einfach mal als 'Horizontale-Parallele-Linien-Strateigie' bezeichne, funktioniert wie folgt.
Ich verwende mal ein bisschen Pseudo-Code um den Algorithmus zu beschreiben.

for (1<= ziffer <= 9) {
for (betrachte jeweils die 3 oberen bzw. mittleren bzw. unteren Zeilen) {
1.Prüfe ob die Ziffer in mindestens 2 der 3 9er-Bllöcke vorhanden ist.
2. Falls ja, ist zumindest die Zeile in der die Ziffer im dritten 9er-Block stehen muss identifiziert.
3. Prüfe die Anzahl der freien Stellen in dieser Zeile dieses Blocks.
Ist diese == 1. ---> fertig Position der Ziffer ist bestimmt.
Ist diese == 2. ---> prüfe ob in einer der zu diesen freien Stellen zugehörigen Spalten die Ziffer bereits steht. Falls ja kann ich diese ausschliessen und somit ist die Position eindeutig bestimmt.
Ist diese == 3. ---> prüfe ob in 2 der 3 zu den freien Stellen zugehörigen Spalten die Ziffer bereits steht. Falls ja kann ich diese wieder ausschliessen, und die Position ist wieder eindeutig bestimmt.
}
}
Analog gibt es auch eine Vertikale-Parallele-Linien-Strateigie' die ebenfalls angewandt wird.
Diese Strategien funktionieren auch sehr gut und schnell wenn man selbst (als Mensch) ein Sudoku lösen will.

josefYY
2006-04-06, 15:57:24
In deinem Fall machst es wohl nicht so viel Sinn, aber im allgemeinen würde ich dir vorschlagen, dein Spielfeld als Objekt zu modellieren, und mit entsprechenden Zugriffsmethoden auszustatten. Wenn du das richtig drehst, kannst du dann das selbe Objekt zum Aufbau deiner grafischen Oberfläche verwenden.

Merke: alles was sich wie ein Objekt anfühlt, sollte auch ein Objekt sein! ;)
Ich merke du hast das Thema OO mit der Muttermilch aufgesogen :biggrin:
Spass beiseite: Ich finde die Idee gut. Ich merke an mir selbst aber schon sehr deutlich das der prozedurale Programmierstil den ich habe, nur sehr schwer auszumerzen sein wird :smile:

Monger
2006-04-06, 17:27:25
Ich merke du hast das Thema OO mit der Muttermilch aufgesogen :biggrin:

Ganz im Gegenteil: ich hatte einen langen Weg von QBasic über Pascal, Delphi, C, C++ und letztendlich Java, um zum wahren Glauben zu finden! :D

SamStone
2006-04-06, 17:55:42
Nicht ganz. Der von dir genannte Ansatz ist nur eine von 3 Strategien die ich verwende. Bin mir nicht ganz sicher ob diese alleine ausreichen würde.
Ne tut es bei manchen Sudokus nicht. Deswegen sagte ich ja +Backtracking:
Immer wenn man an eine Stelle gerät, an der man sich nichtmehr sich sein kann, dass irgendwo eine bestimmte Zahl hinkommt, dann probiert setzt man für diese Stelle probeweise ein, und verfährt mit dem Algorithmus wie vorher, bis man in eine "Sackgasse" gerät, und probiert es dann mit der nächsten Zahl noch einmal.

Trap
2006-04-06, 18:04:32
Das mit den 30 Zeilen meinst du doch nicht ernst, oder?
Doch (aus http://groups.google.com/group/comp.lang.lisp/tree/browse_frm/thread/c921bf275dce225d/cd1a5d14d506275a?rnum=1&hl=en ):
(defun digits-in-region (sudoku x y)
(loop repeat 3 for x from (* 3 (floor x 3))
append (loop repeat 3 for y from (* 3 (floor y 3))
collect (aref sudoku y x))))

(defun digits-in-row (sudoku y)
(loop for x from 0 below 9 collect (aref sudoku y x)))

(defun digits-in-column (sudoku x)
(loop for y from 0 below 9 collect (aref sudoku y x)))

(defun remove-zeros (list) (remove-if #'zerop list))

(defun possible-digits (sudoku x y)
(set-difference
'(1 2 3 4 5 6 7 8 9)
(remove-zeros
(union (digits-in-region sudoku x y)
(union (digits-in-row sudoku y)
(digits-in-column sudoku x))))))

(defun solve-next (sudoku x y)
(when (= y 9) (throw 'done sudoku))
(multiple-value-bind (nextx nexty)
(if (< x 8) (values (1+ x) y) (values 0 (1+ y)))
(if (/= 0 (aref sudoku y x))
(solve-next sudoku nextx nexty)
(dolist (digit (possible-digits sudoku x y))
(setf (aref sudoku y x) digit)
(solve-next sudoku nextx nexty)
(setf (aref sudoku y x) 0)))))

(defun solve (sudoku)
(pprint (catch 'done
(solve-next (make-array '(9 9) :initial-contents sudoku) 0 0))))
Eine Lösung mit 19 Zeilen gibts da: http://www.ffconsultancy.com/free/sudoku/ (die find ich aber nicht sonderlich lesbar).

Kinman
2006-04-06, 18:14:19
Doch (aus http://groups.google.com/group/comp.lang.lisp/tree/browse_frm/thread/c921bf275dce225d/cd1a5d14d506275a?rnum=1&hl=en ):
... cut
Eine Lösung mit 19 Zeilen gibts da: http://www.ffconsultancy.com/free/sudoku/ (die find ich aber nicht sonderlich lesbar).

Ist das Common Lisp?

mfg Kinman

Trap
2006-04-06, 18:20:50
Ist das Common Lisp?
Ja.

HajottV
2006-04-06, 19:10:20
Bah, selber machen ist Trumpf!



////////////////////////////////////////////////////////////////////////////////
private static boolean solve(int row, int col)
////////////////////////////////////////////////////////////////////////////////
{
if (row == 9) {
row = 0;
if (++col == 9) return true;
}

if (intArray[row][col] != 0) return solve(row + 1, col);

int r0 = (row / 3) * 3;
int c0 = (col / 3) * 3;

NOPE:

for (int n = 1; n < 10; n++) {

intArray[row][col] = n;

for (int i = 0; i < 9; i++)
if ( i != col && n == intArray[row][i]
|| i != row && n == intArray[i][col]
|| r0 + (i % 3) != row && c0 + i / 3 != col && n == intArray[r0 + (i % 3)][c0 + i / 3])
continue NOPE;

if (solve(row + 1, col)) return true;
}

intArray[row][col] = 0;

return false;
}


public static void main(String[] args) {
initialisiereStartaufstellung();
ausgabeStartArray();

System.out.println("solution exists? " + solve(0, 0));
ausgabeFinalArray();
System.exit(1);

...


Gruß

Jörg

josefYY
2006-04-06, 21:48:17
Doch (aus http://groups.google.com/group/comp.lang.lisp/tree/browse_frm/thread/c921bf275dce225d/cd1a5d14d506275a?rnum=1&hl=en ):
(defun digits-in-region (sudoku x y)
(loop repeat 3 for x from (* 3 (floor x 3))
append (loop repeat 3 for y from (* 3 (floor y 3))
collect (aref sudoku y x))))

(defun digits-in-row (sudoku y)
(loop for x from 0 below 9 collect (aref sudoku y x)))

(defun digits-in-column (sudoku x)
(loop for y from 0 below 9 collect (aref sudoku y x)))

(defun remove-zeros (list) (remove-if #'zerop list))

(defun possible-digits (sudoku x y)
(set-difference
'(1 2 3 4 5 6 7 8 9)
(remove-zeros
(union (digits-in-region sudoku x y)
(union (digits-in-row sudoku y)
(digits-in-column sudoku x))))))

(defun solve-next (sudoku x y)
(when (= y 9) (throw 'done sudoku))
(multiple-value-bind (nextx nexty)
(if (< x 8) (values (1+ x) y) (values 0 (1+ y)))
(if (/= 0 (aref sudoku y x))
(solve-next sudoku nextx nexty)
(dolist (digit (possible-digits sudoku x y))
(setf (aref sudoku y x) digit)
(solve-next sudoku nextx nexty)
(setf (aref sudoku y x) 0)))))

(defun solve (sudoku)
(pprint (catch 'done
(solve-next (make-array '(9 9) :initial-contents sudoku) 0 0))))
Eine Lösung mit 19 Zeilen gibts da: http://www.ffconsultancy.com/free/sudoku/ (die find ich aber nicht sonderlich lesbar).
Also jetzt bekommt man Ego aber ganz bestimmt einen Knacks :biggrin:
Nicht schlecht.

Aqualon
2006-04-07, 00:56:04
Sollte sich nicht Prolog auch wunderbar für Sudoku eignen?

Aqua

HajottV
2006-04-07, 10:54:14
Oops, da hab ich doch gestern einen eleganten Trick glatt übersehen. :rolleyes:



////////////////////////////////////////////////////////////////////////////////
private static boolean solve(int row, int col)
////////////////////////////////////////////////////////////////////////////////
{
if (row == 9) {
row = 0;
if (++col == 9) return true;
}

if (intArray[row][col] != 0) return solve(row + 1, col);

int r0 = (row / 3) * 3;
int c0 = (col / 3) * 3;

NOPE:

for (int n = 1; n < 10; n++) {

for (int i = 0; i < 9; i++)
if ( n == intArray[row][i]
|| n == intArray[i][col]
|| n == intArray[r0 + (i % 3)][c0 + i / 3])
continue NOPE;

intArray[row][col] = n;

if (solve(row + 1, col)) return true;
}

intArray[row][col] = 0;

return false;
}


public static void main(String[] args) {
initialisiereStartaufstellung();
ausgabeStartArray();

System.out.println("solution exists? " + solve(0, 0));
ausgabeFinalArray();
System.exit(1);
...


Gruß

Jörg[/QUOTE]

Monger
2006-04-07, 11:29:24
Arrghh!! Sehe ich da wirklich ein Goto? :eek:

Das ist ja wie Fallschirmspringen ohne Fallschirm: man kann's machen, aber es endet selten auf eine gesunde Weise...

Bitte mach das weg!

HajottV
2006-04-07, 11:43:39
Arrghh!! Sehe ich da wirklich ein Goto?

Nein.

Gruß

Jörg

Monger
2006-04-07, 11:53:42
Nein.

Gruß

Jörg

Okay, mein Fehler. Hab grad mal nachgelesen, und das gelabelte continue war mir bis jetzt tatsächlich völlig unbekannt... wieder was dazugelernt! :)

Chris Lux
2006-04-07, 12:26:23
Okay, mein Fehler. Hab grad mal nachgelesen, und das gelabelte continue war mir bis jetzt tatsächlich völlig unbekannt... wieder was dazugelernt! :)
wieso fehler. du hast das IMO schon richtig erkannt. so wie das da oben eingesetzt wird ist das ein goto.

Monger
2006-04-07, 14:02:49
wieso fehler. du hast das IMO schon richtig erkannt. so wie das da oben eingesetzt wird ist das ein goto.

Nein, ist es nicht. Wäre es das, wäre es eine Endlosschleife. Ein goto sieht normalerweise so aus (jetzt bezogen auf das Sudoku Programm):



SPRUNGMARKE:
for(int i = 0; i < 9; i++){
if (x == 0) goto SPRUNGMARKE;
}

Wenn das eine Sprungmarke wäre, würde das Programm jedes mal in die Schleife reinspringen, von dort auf Sprungmarke zurückspringen, wieder in die Schleife von Beginn an rein usw.
Ein goto ist ja ein echter Zeilensprung.

Ein continue label sagt nur, welcher Schleifendurchgang abgebrochen werden soll. Deshalb führt der folgende Code eben NICHT zu einer Endlosschleife, sondern nur zu einem vorschnellen Schleifenende:


CONTINUEMARKE:
for(int i = 0; i < 9; i++){
if(x==0) continue CONTINUEMARKE;
}


Ich habs bis jetzt auch nicht gewusst, aber für manche Schleifenkonstruktionen dürfte das ganz praktisch sein.

SamStone
2006-04-07, 16:15:01
Ein continue label sagt nur, welcher Schleifendurchgang abgebrochen werden soll. Deshalb führt der folgende Code eben NICHT zu einer Endlosschleife, sondern nur zu einem vorschnellen Schleifenende:


CONTINUEMARKE:
for(int i = 0; i < 9; i++){
if(x==0) continue CONTINUEMARKE;
}


Ich habs bis jetzt auch nicht gewusst, aber für manche Schleifenkonstruktionen dürfte das ganz praktisch sein.
Ist dann aber, wenn ich es jetzt richtig verstanden habe, trotzdem unstrukturierte Programmierung, und damit genauso zu vermeiden wie ein GoTo.

Monger
2006-04-07, 17:07:55
Ist dann aber, wenn ich es jetzt richtig verstanden habe, trotzdem unstrukturierte Programmierung, und damit genauso zu vermeiden wie ein GoTo.

Nein. Continue und break sind einfach Hilfsmittel, um den Schleifenaufbau zu vereinfachen. Du könntest das obige Beispiel auch anders schreiben:


for(int i = 0; i < 9; i++){
if(x==0) {}
else {
// hier irgendwas tun
}
}


Der Sinn des continue (genau wie des break) ist ja, bestimmte Programmteile zu umgehen. Durch geschickte Klammerung kann man genau das selbe erreichen, nur kann es dadurch etwas komplizierter werden.

Ich hab in meinem gesamten Leben noch nie ein continue verwendet, bis jetzt habe ich es immer über irgendwelche Klammergebirge hingekriegt. Aber das eine ist mit dem anderen gleichbedeutend.

SamStone
2006-04-07, 19:28:23
Nein. Continue und break sind einfach Hilfsmittel, um den Schleifenaufbau zu vereinfachen.
Ja eben, breaks sind ja auch unstrukturiert.

Du könntest das obige Beispiel auch anders schreiben:


for(int i = 0; i < 9; i++){
if(x==0) {}
else {
// hier irgendwas tun
}
}

Aber irgendwie seh ich jetzt nicht, wass das Label bei dem Continue für einen Sinn hat. Ohne dieses Label würde sich das doch genau so verhalten, oder? :|

Xmas
2006-04-07, 20:12:36
Ja eben, breaks sind ja auch unstrukturiert.
Aber praktisch.


Aber irgendwie seh ich jetzt nicht, wass das Label bei dem Continue für einen Sinn hat. Ohne dieses Label würde sich das doch genau so verhalten, oder? :|
Nein, continue ohne Label springt nur zum nächsten Durchlauf der innersten Schleife. Hier sind aber zwei geschachtelte Schleifen, und das continue mit Label bricht die innere Schleife ab und geht zum nächsten Durchlauf der äußeren Schleife.

Der_Donnervogel
2006-04-07, 20:17:26
Das mit den statischen Methoden ist nur weil die main-Methode statisch ist, und ich somit auch nur statische-Methoden aufrufen kann.
Wenn man wollte könnte man das leicht vermeiden, zb:


public class Sudoku {
static int intArray[][] = new int[9][9];
static int startArray[][] = new int[9][9];

public Sudoku() {
initialisiereStartaufstellung();
ausgabeStartArray();

while (raetselNichtGeloest()) {
hPLinienStrategie();
vPLinienStrategie();
streicheZifferStrategie();
}
ausgabeFinalArray();
}

public static void main(String[] args) {
new Sudoku();
}


und schon müßten die ganzen Funktionen nicht mehr statisch sein.

Monger
2006-04-08, 01:03:59
Ja eben, breaks sind ja auch unstrukturiert.

Nein, eigentlich nicht. Ein break ist immer nur auf ein Label im selben Scope anwendbar. Anders als bei Goto kann man also nicht in irgendwelche völlig fremden Programmteile reinspringen.

Es ist genauso eine Operation wie return. Was ein break, continue oder return macht, ist fest an den derzeitigen Kontext gekoppelt, und verlässt diesen auch nie.

SamStone
2006-04-08, 11:38:31
Nein, eigentlich nicht. Ein break ist immer nur auf ein Label im selben Scope anwendbar. Anders als bei Goto kann man also nicht in irgendwelche völlig fremden Programmteile reinspringen.

Es ist genauso eine Operation wie return. Was ein break, continue oder return macht, ist fest an den derzeitigen Kontext gekoppelt, und verlässt diesen auch nie.
Wenn man z.B. eine Schleife von 1 bis 10 macht, dann erwartet man, dass diese genau 10 mal durchlaufen wird. Wenn man da jetzt einfach wild mit einem break rausspringt, dann ist dies unstrukturiert.

Stell dir einmal vor du willst den Zustand eines Programmes in irgend einem bestimmten Zustand irgendwie erfassen. Wenn du nur eine einfache Sequenz hast, dann ist das relativ einfach: du musst dir einfach nur merken in welcher "Zeile" man ist.
Wenn man sich das innerhalb von Schleifen anguckt, wird das komplizierter. Hier musst du dir auch merken wie oft diese Schleife schon abgelaufen ist.

Du, als Programmierer, hast also ganz bestimmte "Koordinaten" im Kopf, die dir dabei helfen den bestimmten Zustand zu erfassen.

Wenn man jetzt so etwas wie break oder continue verwendet, dann nimmt man als Programmierer diese Koordinaten selbst in die Hand (also man manipuliert diese), und somit ist es unstrukturiert.

So seh ich das jedenfalls (bzw. so hab ich es gelernt :tongue: )

Trap
2006-04-08, 12:21:19
Ich halte break/continue für lesbarer als die Alternative: Statusvariable hinzufügen und das Verhalten von Hand nachbauen

for(int i=0;i<10;++i)
{
if(f(x[i])) break;
//irgendwas
}
wird zu
for(int i=0, bool done=false;i<10 && !done ;++i)
{
if(f(x[i])) {done=true;}
else {
//irgendwas
}
}

Die Garantie, dass eine for-schleife komplett durchläuft hat man in Sprachen mit Exceptions sowieso nicht.

Monger
2006-04-08, 12:36:18
Wenn man z.B. eine Schleife von 1 bis 10 macht, dann erwartet man, dass diese genau 10 mal durchlaufen wird. Wenn man da jetzt einfach wild mit einem break rausspringt, dann ist dies unstrukturiert.

Und was ist mit return? Damit kannst du jederzeit aus deiner Methode rausspringen. Wenn du das an eine Bedingung koppelst, kannst du (von außen betrachtet) nie vorhersagen wann die Methode enden wird.

Stell dir einmal vor du willst den Zustand eines Programmes in irgend einem bestimmten Zustand irgendwie erfassen. Wenn du nur eine einfache Sequenz hast, dann ist das relativ einfach: du musst dir einfach nur merken in welcher "Zeile" man ist.
Wenn man sich das innerhalb von Schleifen anguckt, wird das komplizierter. Hier musst du dir auch merken wie oft diese Schleife schon abgelaufen ist.

Das kannst du in einer modernen Sprache sowieso nicht mehr. Du kannst einfach nicht mehr vorhersagen, welchen Weg ein Programm nehmen wird. Das ist ja auch ein Stück Freiheit: als Entwickler gibst du nur vor was das Programm kann - was es letztendlich wirklich tut, hängt im großen Maße davon ab in welcher Umgebung es sich bewegt, und wie der Benutzer sich verhält.

Oder andersrum gesagt: wenn ich dir jetzt ein Programm zum debuggen gebe, und du schaust dir den Breakpoint an wo es gerade hängt, dann kannst du unmöglich sagen was das Programm bis jetzt getan hat. Es entzieht sich einfach deiner Kontrolle.

SamStone
2006-04-08, 12:52:48
Ich halte break/continue für lesbarer als die Alternative: Statusvariable hinzufügen und das Verhalten von Hand nachbauen

for(int i=0;i<10;++i)
{
if(f(x[i])) break;
//irgendwas
}
wird zu
for(int i=0, bool done=false;i<10 && !done ;++i)
{
if(f(x[i])) {done=true;}
else {
//irgendwas
}
}
So ist das auch ungeschickt. Das würde man dann eher zu sowas umändern:
int i = 0;
while (!x[i])
{
//Irgendwas
i++;
}
So ist es strukturiert und lesbar.

Die Garantie, dass eine for-schleife komplett durchläuft hat man in Sprachen mit Exceptions sowieso nicht.
Ja das stimmt schon, aber die sollte man ja sowieso nur in Ausnahmesituationen werfen... Der Programmierer weiß also, dass der gerade laufende Algorithmus komplett abgebrochen wird und irgendwo die exception dann abgefangen wird.

Aber stimmt schon, moderne Sprachen kommen kaum ohne solche wilden Sprünge aus. Trotzdem habe ich mittlerweile eingesehen, dass ich solche Sachen wie continue und break nicht benutze (jedenfalls wenn ich gerade in einer "Ich muss schönen Code schreiben" Phase bin).

Und was ist mit return? Damit kannst du jederzeit aus deiner Methode rausspringen. Wenn du das an eine Bedingung koppelst, kannst du (von außen betrachtet) nie vorhersagen wann die Methode enden wird.
Ja eben, deswegen ist ein rausspringen mit return auch unstrukturiert. Ich weiß, dass man in C return benutzen muss um ein Wert zurück zu geben. Dass sollte man dann halt ganz am ende machen, wenn man es strukturiert haben will. In Pascal gibt es zum zurückgeben ja zum Beispiel einfach für jedes Unterprogramm eine bestimmte Variable die man setzen kann, ohne dadurch dann rauszuspringen.

Strukturiert ist Code dann, wenn man ihn in eine Struktographik packen kann. Und in einer solchen gibt es halt sowas wie return, break oder continue nicht.

EDIT: Verdammt ich sehe gerade das da oben müsste natürlich eher
int i = 0;
while (!x[i] && i<10)
{
//Irgendwas
i++;
}
heißen.
Das könnte man in C dann natürlich auch so schreiben:
for(int i=0; !x[i], i<10; ++i)
{
//Irgendwas
}
Der Punkt ist halt der, dass du schon bei der Definition deiner schleife klar machst, wass die eigentliche Abbruchsbedingung ist, und nicht erst vortäuschst, dass es sich dabei um eine Schleife handelt deren Inhalt genau 10 mal abgearbeitet wird.

Xmas
2006-04-08, 16:13:26
Oder andersrum gesagt: wenn ich dir jetzt ein Programm zum debuggen gebe, und du schaust dir den Breakpoint an wo es gerade hängt, dann kannst du unmöglich sagen was das Programm bis jetzt getan hat. Es entzieht sich einfach deiner Kontrolle.
Dafür gibts ja Omniscient Debugging.

So ist das auch ungeschickt. Das würde man dann eher zu sowas umändern:
int i = 0;
while (!x[i])
{
//Irgendwas
i++;
}
So ist es strukturiert und lesbar.
Das ist aber gar nicht dasselbe. Deine Edits auch nicht.

Der Punkt ist halt der, dass du schon bei der Definition deiner schleife klar machst, wass die eigentliche Abbruchsbedingung ist, und nicht erst vortäuschst, dass es sich dabei um eine Schleife handelt deren Inhalt genau 10 mal abgearbeitet wird.
Das tust du bei C-ähnlichen Sprachen sowieso nicht. Und ein continue ist auch kein Schleifenabbruch.

Expandable
2006-04-08, 16:39:05
Also ich benutze gerne break, continue und return um frühzeitig aus meiner Funktion bzw. Schleife zu springen (oder einen Schleifenablauf zu überspringen). Das ist einfach schnell und übersichtlicher als die ganzen ifs und elses.

SgtTynis
2006-04-08, 16:57:33
Ich sehe das aehnlich; fuer was hat man sonst solche Elemente in der Sprache wenn man sich nicht nutzen soll. Es gibt selbst Anwendungsfaelle fuer goto; selten, aber es gibt sie. Gute Lesbarkeit oder auch Erweiter- und Wiederverwendbarkeit laesst sich nicht durch das blinde Einhalten bestimmter Paradigmen erzielen. Irgendwo muss man als Programmierer kreativ sein. Sicher auch einer der Gruende warum generierte Sourcecode meist auch als solcher zu erkennen ist.

Neomi
2006-04-08, 17:32:31
Also ich benutze gerne break, continue und return um frühzeitig aus meiner Funktion bzw. Schleife zu springen (oder einen Schleifenablauf zu überspringen). Das ist einfach schnell und übersichtlicher als die ganzen ifs und elses.

Dito. Und auch goto nutze ich, wenn der Code dadurch deutlich lesbarer wird. Das ist zwar wirklich sehr selten (in meinen Projekten zumindest), aber trotzdem möglich. Man sollte den Sinn oder Unsinn solcher Sachen immer von der jeweiligen Situation abhängig machen. Etwas generell zu verteufeln dürfte generell falsch sein.

SamStone
2006-04-08, 18:34:42
Das ist aber gar nicht dasselbe. Deine Edits auch nicht.
Dann halt
while (!f(x[i]) && i<10)
Is ja egal, das Prinzip ist klar.

Das tust du bei C-ähnlichen Sprachen sowieso nicht.
Hehe ja stimmt, in C gibt es ja eigentlich keine richtige Zählschleife. Deswegen ist das auch ein bischen komisch das daran alles zu erklären.
Und ein continue ist auch kein Schleifenabbruch.
Nein, aber da überspringt man einfach für bestimmte Durchläufe die Bearbeitung von den Codepassagen die danach kommen. Mit einem if mach man das viel deutlicher, dass bestimmter Code nur unter einer bestimmten Bedingung abgearbeitet wird.


Irgendwo muss man als Programmierer kreativ sein.
Ja eben, man sollte genug Kreativität aufbringen, gotos zu vermeiden, anstatt dumm zu der Holzhammermethode zu greifen, und wild hin und her zu springen.

Xmas
2006-04-08, 19:18:04
Dann halt
while (!f(x[i]) && i<10)
Is ja egal, das Prinzip ist klar.
Das wird aber komplizierter, wenn das break erst in mehreren verschachtelten ifs vorkommt.

Nein, aber da überspringt man einfach für bestimmte Durchläufe die Bearbeitung von den Codepassagen die danach kommen. Mit einem if mach man das viel deutlicher, dass bestimmter Code nur unter einer bestimmten Bedingung abgearbeitet wird.
Ebenso hier.

Aber noch ein Beispiel zum continue mit Label:
label: while(a())
{
while(b())
{
if(c()) continue label;
d();
}
e();
}

while(a())
{
bool bContinue = false;
while(!bContinue && b())
{
if(c()) bContinue = true;
else d();
}
if(!bContinue) e();
}
Da finde ich erstere Variante besser.

Ja eben, man sollte genug Kreativität aufbringen, gotos zu vermeiden, anstatt dumm zu der Holzhammermethode zu greifen, und wild hin und her zu springen.
Wen goto, break, continue oder return lesbarer sind, dann gibt es doch keinen Grund sie zu vermeiden.