# Minesweeper



## Swim (20. Jan 2012)

Hallo Java-Freunde,

ich bin gerade dabei Minesweeper zu programmieren, jedoch hab ich noch ein großes Problem: 
Ich weiß nicht, wie ich es anstellen soll, dass sich mehrere Felder gleichzeitig öffnen.
Bis jetzt funktionier alles sehr gut und wenn man das Programm ausführt, zeigt es die Minen richtig an und man kann auch die Buttons drücken und es erscheint der richtige Text dazu.
Ich hoffe es kann mir jemand helfen:
Es sind insgesamt 5 Dateien:

Hier werden die Parameter festgelegt:
[Java]public class Param {

	static final int Fensterhöhe = 800;
	static final int Fensterbreite = 800;
	static final int Felderhöhe = 10;
	static final int Felderbreite = 10;
	static final int mines = 10;

}[/code]


Hier sind die Zustände die ein Button annehmen kann:

[Java]
public enum Entry {

	Mine, Nix, Eins, Zwei, Drei, Vier, Fünf, Sechs, Sieben, Acht, Flagge;

	public String toString(){
		if(this == Mine){
			return this.name();
		}

		if(this == Nix){
			return "";
//			return this.name();
		}
		if(this == Eins){
			return "1";
		}
		if(this == Zwei){
			return "2";			
		}
		if(this == Drei){
			return "3";
		}
		if(this == Vier){
			return "4";
		}
		if(this == Fünf){
			return "5";
		}
		if(this == Sechs){
			return "6";
		}
		if(this == Sieben){
			return "7";
		}
		if(this == Acht){
			return "8";	
		}
		if(this == Flagge){
			return "F";			
		}else{
			return this.name();
		}

	}

	public void setText(String a) {


	}



}[/code]


Das hier ist meine Logic, wo zum Beispiel berechnet wird, was passiert, wenn ich an den Rändern klicke.

[Java]

import java.util.Random;

public class MineLogic {

	private Entry[][] board;
	private int counter = 0;
	private int c = 0;
	private Random r = new Random();

	public MineLogic() {

		board = new Entry[Param.Felderhöhe][Param.Felderbreite];

		for (int l = 0; l < Param.Felderhöhe; l++) {
			for (int m = 0; m < Param.Felderbreite; m++) {
				board[l][m] = Entry.Nix;
			}
		}

		while (counter < Param.mines) {
			int s = r.nextInt(Param.Felderhöhe);
			int s2 = r.nextInt(Param.Felderbreite);

			if (board[s2] == Entry.Nix) {
				board[s2] = Entry.Mine;
				counter++;
			}
		}

	}

	public void setText(String text){

	}

	public String getAsString(int x, int y) {
		return board[x][y].toString();

	}

	public String getAsString(Entry a) {
		return a.toString();

	}

	public Entry get(int x, int y) {
		return board[x][y];
	}




	public boolean set(int x, int y) {
		if (board[x][y] == Entry.Nix) {

			// Für die rechte untere Ecke gilt:

			while (x == board.length - 1 && y == board[x].length - 1) {
				if (board[x - 1][y] == Entry.Mine) {
					c++;
				}
				if (board[x][y - 1] == Entry.Mine) {
					c++;
				}
				if (board[x - 1][y - 1] == Entry.Mine) {
					c++;
				}

				board[x][y] = umwandeln(c);

				c = 0;
				return true;
			}

			// Für linke untere Ecke gilt:

			while (x == board.length - 1 && y == 0) {

				if (board[x][y + 1] == Entry.Mine) {
					c++;
				}

				if (board[x - 1][y] == Entry.Mine) {
					c++;
				}

				if (board[x - 1][y + 1] == Entry.Mine) {
					c++;
				}

				board[x][y] = umwandeln(c);
				c = 0;
				return true;
			}

			// Für linke obere Ecke gilt:

			while (x == 0 && y == 0) {
				if (board[x][y + 1] == Entry.Mine) {
					c++;
				}
				if (board[x + 1][y + 1] == Entry.Mine) {
					c++;
				}
				if (board[x + 1][y] == Entry.Mine) {
					c++;
				}
				board[x][y] = umwandeln(c);
				c = 0;
				return true;
			}

			// Für rechte obere Ecke gilt:

			while (x == 0 && y == board[x].length - 1) {
				if (board[x + 1][y] == Entry.Mine) {
					c++;
				}
				if (board[x + 1][y - 1] == Entry.Mine) {
					c++;
				}
				if (board[x][y - 1] == Entry.Mine) {
					c++;
				}
				board[x][y] = umwandeln(c);
				c = 0;
				return true;
			}

			// Für Unten gilt:

			while (x == board.length - 1) {
				if (board[x][y + 1] == Entry.Mine) {
					c++;
				}
				if (board[x - 1][y] == Entry.Mine) {
					c++;
				}
				if (board[x][y - 1] == Entry.Mine) {
					c++;
				}
				if (board[x - 1][y - 1] == Entry.Mine) {
					c++;
				}
				if (board[x - 1][y + 1] == Entry.Mine) {
					c++;
				}
				board[x][y] = umwandeln(c);
				c = 0;
				return true;
			}

			// Für Rechts gilt:

			while (y == board[x].length - 1) {

				if (board[x + 1][y] == Entry.Mine) {
					c++;
				}
				if (board[x - 1][y] == Entry.Mine) {
					c++;
				}
				if (board[x][y - 1] == Entry.Mine) {
					c++;
				}
				if (board[x - 1][y - 1] == Entry.Mine) {
					c++;
				}
				if (board[x + 1][y - 1] == Entry.Mine) {
					c++;
				}
				board[x][y] = umwandeln(c);
				c = 0;
				return true;
			}

			// Für Oben gilt:

			while (y == 0) {

				if (board[x + 1][y] == Entry.Mine) {
					c++;
				}
				if (board[x][y + 1] == Entry.Mine) {
					c++;
				}
				if (board[x + 1][y + 1] == Entry.Mine) {
					c++;
				}
				if (board[x - 1][y + 1] == Entry.Mine) {
					c++;
				}
				if (board[x - 1][y] == Entry.Mine) {
					c++;
				}
				board[x][y] = umwandeln(c);
				c = 0;
				return true;
			}

			// Für Links gilt:

			while (x == 0) {

				if (board[x + 1][y] == Entry.Mine) {
					c++;
				}
				if (board[x][y + 1] == Entry.Mine) {
					c++;
				}
				if (board[x + 1][y + 1] == Entry.Mine) {
					c++;
				}
				if (board[x][y - 1] == Entry.Mine) {
					c++;
				}
				if (board[x + 1][y - 1] == Entry.Mine) {
					c++;
				}
				board[x][y] = umwandeln(c);
				c = 0;
				return true;
			}

			// Wenn Entry.Nix nicht am Rand liegt wird das ausgeführt:

			if (board[x + 1][y] == Entry.Mine) {
				c++;
			}
			if (board[x][y + 1] == Entry.Mine) {
				c++;
			}
			if (board[x + 1][y + 1] == Entry.Mine) {
				c++;
			}
			if (board[x - 1][y] == Entry.Mine) {
				c++;
			}
			if (board[x][y - 1] == Entry.Mine) {
				c++;
			}
			if (board[x - 1][y - 1] == Entry.Mine) {
				c++;
			}
			if (board[x + 1][y - 1] == Entry.Mine) {
				c++;
			}
			if (board[x - 1][y + 1] == Entry.Mine) {
				c++;
			}
			board[x][y] = umwandeln(c);
			c = 0;
			return true;
		}

		if (board[x][y] == Entry.Mine) {
			return true;
		}
		return false;
	}

	// Hier wird die Anzahl an Counts in den entsprechenden Eintrag umgeändert.

	public Entry umwandeln(int c) {

		if (c == 1) {
			return Entry.Eins;
		}
		if (c == 2) {
			return Entry.Zwei;
		}
		if (c == 3) {
			return Entry.Drei;
		}
		if (c == 4) {
			return Entry.Vier;
		}
		if (c == 5) {
			return Entry.Fünf;
		}
		if (c == 6) {
			return Entry.Sechs;
		}
		if (c == 7) {
			return Entry.Sieben;
		}
		if (c == 8) {
			return Entry.Acht;
		} else {

			return Entry.Nix;
		}
	}

}
[/code]

Hier werden die Buttons festgelegt ( Hier versuch ich an dem Problem zu arbeiten)
[Java]package Minesweeper;

import java.awt.Button;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JOptionPane;


public class MineButton extends JButton {


	private int x;
	private int y;
	private MineLogic logic;
	Object a;

	public MineButton(MineLogic logic, int x, int y) {
		this.x = x;
		this.y = y;
		this.logic = logic;

		for (int l = 0; l < this.x; l++) {
			for (int m = 0; m < this.y; m++) {

			}
		}

		addMouseListener(new MyMouseAdapter());
	}






	class MyMouseAdapter extends MouseAdapter {
		public void mouseClicked(MouseEvent e) {


			  if (e.getButton() == MouseEvent.BUTTON1) { 
				  // Linksklick 


				  if (logic.set(x, y)){

				  setMargin(new Insets(0,0,0,0));
				  setEnabled(false);
				  setText(logic.getAsString(x,y));
				  setVisible(true);

				  if(logic.get(x, y) == Entry.Mine){
					  JOptionPane.showInputDialog("Blöd gelaufen, du hast Verloren!");

				  }
				}


			  } 
			 if (e.getButton() == MouseEvent.BUTTON3) {
				// Rechtsklick
				 if(logic.get(x, y) == Entry.Flagge){
				 setMargin(new Insets(0,0,0,0));
				 setText(logic.getAsString(Entry.Nix));
				 setVisible(true);
				 }else{
					 setMargin(new Insets(0,0,0,0));
					 setText(logic.getAsString(Entry.Flagge));
					 setVisible(true);
				 }


			}
		}

		public void mouseReleased(MouseEvent e){

		}



	} 

}
[/code]


Hier wird nur das Spiel vorbereitet und erzeugt:
[Java]package Minesweeper;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JFrame;


public class Mine extends JFrame{



	public Mine(){

		setTitle("Minesweeper");
		setSize(Param.Fensterbreite, Param.Fensterhöhe);

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


		MineLogic logic = new MineLogic();
		Container container = getContentPane();
		container.setLayout(new GridLayout(Param.Felderhöhe,Param.Felderbreite));

		for(int y = 0; y < Param.Felderhöhe; y++){
			for(int x = 0; x < Param.Felderbreite; x++){
				container.add(new MineButton(logic, x,y));				
			}
		}

		setVisible(true);

	}

	public static void main(String[] args){
		Mine m = new Mine();
	}

}[/code]


----------



## truesoul (20. Jan 2012)

Ohne mir jetzt den Quelltext angeschaut zu haben würde ich sagen das du z.B ein Backtracking-Algorithmus dafür verwenden könntest. 

Gruß


----------



## Gast2 (20. Jan 2012)

> Ich weiß nicht, wie ich es anstellen soll, dass sich mehrere Felder gleichzeitig öffnen.


Das kannst du zum Beispiel rekursiv lösen. Wenn das Feld auf dem geklickt wurde leer ist, also keine Zahl / keine Mine drunter, dann bewegst du dich rekursiv in alle 4 Richtungen.
Triffst du auf ne Zahl deckst du das Feld noch auf und brichst die Rekursion ab.
Triffst du auf nen weiteres leeres Feld, gehst du wieder in alle 4 Richtungen weiter.
Triffst du auf nen Feld das bereits aufgedeckt ist machst du gar nichts.


----------



## Helgon (20. Jan 2012)

Vielleicht hilft dir das

Minesweeper


----------



## irgendjemand (20. Jan 2012)

warum muss ich bei Minesweeper immer an das hier denken : Minesweeper - The Movie - YouTube


----------



## Swim (21. Jan 2012)

Vielen Dank schon mal.
Also mir ist das Prinzip eigentlich schon klar: Ich muss einfach bei dem aufgedeckten Button schauen, ob um ihn herum weitere Buttons mit einer Null sind und die ebenfalls aufdecken und so weiter...

Aber ich hab keinen Plan wie ich das in Java umsetzten soll. Ich müsste irgendwie Zugriff auf die Buttons bekommen nur wie???


----------



## Fu3L (21. Jan 2012)

Hab deine Quelltexte auch nicht gelesen, aber generell: Speichere die Buttons alle nochmal extra in einer Liste oder einem Array (vllt zweidimensional).  Dann kannst du über die Indexe des Arrays oder der Liste auf die Buttons zugreifen.

Also beim 2D Array wärs in etwa so:


```
int jetzigerButtonX = 2;
int jetzigerButtonY = 3;
for(int x = jetzigerButtonX-1; x < jetzigerButtonX+1; x++) {
   for(int y = jetzigerButtonY-1; y jetzigerButtonY+1; y++) {
       //Hier prüfen, ob eigenes Feld
      //Hier prüfen, ob im Array
      //Hier aufdecken und co
      //zB: buttons[x][y].isMine()
    }
}
```


----------



## Swim (21. Jan 2012)

Wo und wie soll ich das implementieren? 
Vorallem wie schreibe ich ein Button (int?) Array? Mit welchem Inhalt?


----------



## Gast2 (21. Jan 2012)

Du solltest auf jedenfall dein Model von deinem View trennen, also nicht anfangen irgendwelche Button[] Arrays erstellen wo du dann irgendwie an die Information kommst ob Bombe, leer oder Zahl.

Erstell dir ein Array aus int's bzw. aus enums (würde sich hier anbieten). Das kannst du dann auch wunderbar einfach testen. Einfache main, Ausgabe auf Konsole, etc.


----------



## Swim (21. Jan 2012)

Wenn ich jetzt ein Array mit ints mache 
	
	
	
	





```
button = new int[x][y];
		for (int l = 0; l < this.x; l++) {
			for (int m = 0; m < this.y; m++) {
				button[l][m] = 1;
			}
		}
```
dann verstehe ich nicht ganz, was ich damit anfangen kann. 
Ich hätte gerne eine Objekt, das ich dann in meiner "mouseClicked"-Methode aufrufen kann und mit
	
	
	
	





```
setEnabled(false);
				  setText(logic.getAsString(x,y));
				  setVisible(true);
```
versehen kann. Versteht ihr was ich meine? Ich will einfach nur meine Buttons aufrufen um diese dann "einzudrücken" und einen Text darunter auszugeben.


----------



## Fu3L (21. Jan 2012)

Du hast doch deine Klasse MineButton, dann machste damit ein Array vom Typ MineButton:


```
MineButton[][] mines = new MineButton[20[20]; //Wäre das Feld 20 Objekte hoch und breit)
```

Dann kansnt du das anwenden, was ich schrieb, wenn du den index des gedrückten Buttons (x und y werte) kennst.
Natürlich wäre es schön, wenn Modell von View getrennt wäre, aber von dem, was du bisher hast, wäre das das einfachste.

Allerdings muss ich sagen, auch nur den Code überflogen zu haben, mein eigenes Progrämmchen strengt mich grad zu sehr an^^


----------



## Swim (21. Jan 2012)

Und wenn ich dann dieses Array erstellt habe,

```
MineButton[][] button = new MineButton[Param.Felderhöhe][Param.Felderbreite];
```

aber leider immer noch nicht mit z.B. setText ausgeben.

```
setText(logic.getAsString(x,y));
```

PS.: Ich bin ein ziemlicher Neuling was Java anbelangt.


----------



## Fu3L (21. Jan 2012)

Es wäre besser, wenn jeder MineButton, der ja quasie ein Feld repräsentiert, selbst wüsste, ob eine Mine unter ihm liegt oder wie viele um ihn rum sind. Dann könntest du nämlich eine Methode reinsetzen, mit der du abfragen kannst, ob der Button aufgedeckt werden sollte. Wenn ja, markierst du dieses Feld als betrachtet. Dann führst du die oben geschriebene Schleife nochmal aus Sicht dieses Feldes durch und das geht dann so lange weiter, bis alle Felder, die in Frage kommen, betrachtet wurden.


----------



## Swim (21. Jan 2012)

Ah okay, wie schaut die Methode aus, die dann einen Button aufdeckt?


----------



## Swim (22. Jan 2012)

Ich komme leider immer noch nicht weiter...


----------



## Fu3L (22. Jan 2012)

Also zur Darstellung nutzt du moment entweder Bilder oder Zeichen. Egal, welches von beidem der Fall ist, das Vorgehen ist sehr ähnlich:

Wird in der Schleife ein Button darum gebeten, sich aufzudecken, wird eben im Button entweder ein anderes Icon per setIcon oder ein anderer Text per setText() gesetzt. Da mein Vorschlag von vorher lautete, dass jedes Feld wisssen sollte, wie viele Minen es umgeben, weißt du, wie der neue Text lauten sollte.


----------

