Algorithmus Problem in Minesweeper

Crushpest

Mitglied
Hallo zusammen,

nach Tagen des Verzweifelns, hab ich mich entschloßen hier um hilfe zu bitten. Mein Problem:
Ich schreibe an einem Minesweeper und habe mir hierfuer einen Algorithmus ausgedacht, mit dem ich alle leeren Felder um das geklickte Feld aufdecke, wenn dieses auch leer ist.
Bei einer Feldgröße von 10x10 ist das kein problem:
minesweeper10x10.png


Jedoch, sobald der Index im Array 2-stellig wird geht das nichtmehr. Also bei einer Feldgröße ab 11x11 passiert das:
minesweeper11x11.png


Hier der Quellcode:
Minesweeper.java
Feld.java

Ich glaube es liegt irgendwo in diesem Bereich:
Minesweeper.java
[JAVA=300]
private void leerAufdecken(Point point){
Point tmp = null;
//<editor-fold defaultstate="collapsed" desc="Von dir Geklaut 😛">
for (int i = -1; i < 2; i++) {
for (int j = -1; j < 2; j++) {
tmp = new Point(point.x+i, point.y+j);
if (!outOfBound(tmp) && !pruefListe.contains(tmp)) {
pruefListe.add(tmp);
}
}
}
//</editor-fold>
for(Point p : pruefListe){
if(spielFeld[p.x][p.y].aufdecken() == FeldStatus.LEER){//Hier eigentlich minenAnzeige hochzählen, da eventuell schon entschaerft gewesen
leerAufdecken(p);
break;
}
}
pruefListe.clear();
}
[/code]

Feld.java
[JAVA=47]
public FeldStatus aufdecken() {
if (status == FeldStatus.ZU) {
if (minenAußenrum == 0) {
status = FeldStatus.LEER;
} else if (mine) {
this.setBackground(hintergrundGrau);
this.setIcon(bildMinen);
} else {
this.setIcon(null);//Hier eigentlich minenAnzeige hochzählen, da eventuell schon entschaerft gewesen
this.setBackground(hintergrundGrau);
this.setText("" + minenAußenrum + "");
status = FeldStatus.OFFEN;
}
} else if (status == FeldStatus.LEER) {
this.setIcon(null);//Hier eigentlich minenAnzeige hochzählen, da eventuell schon entschaerft gewesen
this.setBackground(hintergrundGrau);
status = FeldStatus.OFFEN;
}
return status;
}
[/code]

Ich hoffe einer von euch kann mir weiterhelfen. Es kommen keine Kompielerfehler oder Exceptions! Also muss es was mit der Logik zu tun haben. Ich bin am Verzweifeln 🙁 !

Das ganze Projekt ist in Netbeans geschrieben. Downloaden kann man es hier:
http://rapidshare.com/files/444891587/Minesweeper.zip
 
Sind doch jeweils 10x10 Felder. Also ich konnte den Bug gerade nicht nachstellen, aber bei mir funktioniert das ganze eh nicht ganz deterministrisch. Manchmal muss mal 5 mal klicken, bis sich was öffnet.
Bau eine Möglichkeit ein die Spielkonfiguration bzw. Konstelation der Minen abzuspeichern und wieder zu laden. Dann spiele solange, bis der Fehler auftritt, dann kann man den nachstellen und genauer überprüfen. Ansonsten könnten noch Debugausgaben helfen.
 
K danke erstmal fuer den Hinweiß, das es immer 10x10 felder sind. Das hatte ich bisher garnicht bemerkt, da ich die feldgroeße im code verändert habe. Werd mich morgen nochmal dransetzten.
 
So auf den ersten Blick ist bei dir die spielFeldErzeugen-Methode falsch:
[JAVA=193]
private void spielFeldErzeugen() {
minenLegen();
panelCenter.removeAll();
panelCenter.setLayout(new GridLayout(schwierigkeitsgrad, schwierigkeitsgrad));
for (int i = 0; i < schwierigkeitsgrad; i++) {
for (int j = 0; j < schwierigkeitsgrad; j++) {
spielFeld[j].setMinenAußenrum(this.wievielMinenUmDasFeld(i, j));
panelCenter.add(spielFeld[j]);
}
}
this.repaint();
}
[/code]
Sollte eher nicht bis
Code:
schwierigkeitsgrad
gehen, sondern über das gesamte Spielfeld-Array.
 
Jaaaa.... das ist ja gar nicht mal so übersichtlich :autsch: Also, ich hab' jetzt eine Weile versucht, nachzuvollziehen, was da passiert, aber ... Hm.

Auch bei 10 Minen tritt manchmal das Problem auf, dass er Minenfelder aufdeckt. Ich wollte die Größe auf 9 reduzieren, aber irgendwas, was vermutlich mit solchen Sachen wie
... schwierigkeitsgrad * (schwierigkeitsgrad / 10)...
zusammenhängt, bewirkt dann, dass er 0 minen verteilt.

Ich habe jetzt mal
Code:
private void leerAufdecken(Point point){
        [b]System.out.println("Decke auf "+point);[/b]
und
Code:
    public FeldStatus aufdecken() {
        if (status == FeldStatus.ZU) {
            if (minenAußenrum == 0) {
                status = FeldStatus.LEER;
            } else if (mine) {
                [B]System.out.println("Decke "+point+" auf, mit mine!");[/B]
                this.setBackground(hintergrundGrau);
                this.setIcon(bildMinen);
...
eingefügt, und ... ja, er deckt eben die Felder mit minen auf, obwohl das bei "leerAufdecken" anfängt - aber ehrlich gesagt hat sich mir noch nicht erschlossen, warum er überhaupt manche Felder NICHT aufdeckt...

Als kleiner Tipp: Math.random() sollte man IMHO nie verwenden. Es macht debugging unmöglich. Du weißt nie, wo die Minen liegen, und kannst es nicht Systematisch testen. Mach' dir stattdessen in der Klasse ein
Code:
private static final Random random = new Random(0);
und rufe da, wo du bisher sowas machst wie
Code:
randomX = (int) (Math.random() * schwierigkeitsgrad);
in Zukunft
Code:
randomX = random.nextInt(schwierigkeitsgrad);
auf. Das Random-Objekt liefert immer die gleiche Folge von Zufallszahlen, abhängig von der Zahl die im Konstruktor übergeben wird (hier die 0). Wenn es "echt" zufällig sein soll, erstellt man ein Random() (ohne Parameter).

Ansonsten ... GUI und Modell zu trennen wäre ein bißchen viel verlangt. Sprechendere Variablennamen und so wären vielleicht nicht verkehrt. Und dann noch "Details": In der leerAufdecken-Methode wird die pruefListe verwendet. Und NUR da. Also sollte sie auch nicht als Variable oben in der Klasse stehen, sondern NUR da erstellt werden, wo sie gebraucht wird:
Java:
class Minesweeper extends JFrame {
....
    // WEG private List<Point> pruefListe = new ArrayList<Point>(); //Fuer Aufdeck-Algorithmus



    private void leerAufdecken(Point point){

        List<Point> pruefListe = new ArrayList<Point>() // HIN
...

        // Kann dann auch weg: pruefListe.clear();
    }
Ganz allgemein: Den Gültigkeitsbereich von Variablen so klein wie möglich halten.


Ich hatte auch mal so ein Programm geschrieben. Bei mir hieß das natürlich "MeinSweeper" 😀 . Die Methode zum aufdecken der Felder hatte ich rekursiv gemacht, im Pseudocode war das GROB sowas wie
Java:
void open(int x, int y)
{
    if (outOfBounds(x,y)) return;
    if (statusOf(x,y)==OPEN) return;
    changeStatusTo(x,y,OPEN);
    if (hasNeighborsWithMines(x,y)) return; 
    open(x-1,y);
    open(x+1,y);
    open(x,y-1);
    open(x,y+1);
}
 
Danke fuer die super schnelle und gute Rueckmeldung. Das problem lag an der spielFeldErzeugen() methode. Jetzt funktioniert es einigermaßen. Ansonsten werd ich die hier vorgeschlagenen veränderungen noch übernehmen.
Sonst wäre vielleicht ein tip fuer die uebersichtlichkeit und variablennamen vergebung ganz praktisch. Oder was euch sonst noch auffällt, was man besser machen koennte.
 

Zurück
Oben