# Schiffeversenken



## sunshine (26. Mrz 2011)

Hallo!
Ich soll Schiffeversenken programmieren. Also ich schreib den Code schon selber aber kann mir vllt jemand ne grobe Anleitung geben mit den Klassen die ich bauch und vltt den wichtigsten Methoden. Ich weiß nämlich nicht so ganz wie ich anfangen soll.
Hat vllt jemand diese Aufgabe in ner Übung gehabt und kann mir die Angabe dazu schicken oder so?


----------



## slawaweis (26. Mrz 2011)

am besten fängt man solche Sachen an, indem man die Regeln des Spieles und den Verlauf eines Beispielspiels einmal im Klartext mit eigenen Worten aufschreibt. Muss erst mal auch nicht strukturiert sein. Hast Du schon so eine Beschreibung des Spieles? Dann bitten posten. Wenn nicht, dann wäre hier ein guter Platz das aufzuschreiben.

Slawa


----------



## kirax (26. Mrz 2011)

Bei sowas bietet es sich immer an die *Darstellung* des Felds und die *Logik* des Spiels getrennt zu halten (=separate Klassen).
Dann: Objekte im Spiel sind immer direkt prädestiniert, als eigene Klassen definiert zu werden.

Mit den beiden Hinweisen kommt man eigentlich schon zu einem vernünftig gekapselten OOP-Ansatz.


----------



## Firephoenix (27. Mrz 2011)

Hi,
der Einstieg von slawaweis ist da genau der richtige: Brainstorming.
Ich kann dir ja mal meine Vorgehensweise wiedergeben, bezogen auf Schiffe versenken:
Zuerstmal musst du dir Gedanken über das Projekt machen, was genau sind die Anforderungen?
-Soll es eine Multiplayer-Anwendung per Netzwerk werden?
-Soll ein CPU-Gegner eingebaut werden?
-Soll es Spielstatistiken geben?
-Wie soll die Oberfläche aussehen (falls es eine gibt)?
Weißt du nun was genau benötigt wird kannst du dir Gedanken über das Datenmodell machen:
-Es gibt: Spieler, das Schiffe-Feld, Schiffe, eine Vorbereitungs und eine Spielphase.
-Vorbereitungsphase: Das Feld wird erstellt
-Spielphase: Es wird auf das Feld geschossen.
Hier haben wir schon den ersten Punkt: Das Feld. Das Feld ist eine x*x tiles-karte die folgende Informationen pro Feld kennen muss:
-Koordinaten des Feldes
-Wasser oder Schiff (falls ja, welches Schiff)
-zusätzlich bietet es sich sicherlich an zu speichern ob schon auf das Feld geschossen wurde.
Das Feld könnte also einen status haben (Wasser_frei, Wasser_beschossen, Schiff_frei, Schiff_beschossen) und gleichzeitig Speichern zu welchem Schiff es gehört.
Somit kommen wir zum Schiff-Objekt.
Ein Schiff besteht aus x Feldern, jedes davon kann zerstört sein. Warum also nicht den Status des Schiffes in ein Array packen? [0]ist das vorderteil [x-1] ist das hinterteil.
Somit haben wir schon unser Schiff-Objekt, ein Name rundet das ganze ab.
Das Spiel selbst muss natürlich auch irgendwie verwaltet werden. Diese Verwaltungsklasse muss folgendes leisten:
Sie muss beide Felder kennen,
sie muss evtl Statistiken speichern oder das Spiel neu starten.
Hat man sich grob Gedanken über die Klassen gemacht geht man die Abläufe durch, schnell wird man merken, das man noch viel vergessen hat:
Bsp: ein Spieler klickt auf eine Zelle im Feld seines Gegners (in dem er die Schiffe nicht sehen darf):
-Die Gui muss den Klick an die Game-Verwaltung senden (Listener/Eventhandling?)
-Das Game muss das Feld des Gegners an der Stelle prüfen.
-Hat der Spieler schon an die Stelle geschossen passiert garnichts
-War die Stelle Wasser gibt es eine Welle  der Gegner ist dran, weitere Klicks des Spielers dürfen nichts auslösen
-War an der Stelle ein Schiff ist der Spieler wieder an der Reihe
Sollte ein Schiff getroffen werden muss man zusätzlich weitere Abfragen erstellen:
-Wurde das Schiff zerstört? Falls ja müssen es beide Spieler sehen
-Dem Gegner muss angezeigt werden wo sein Schiff getroffen wurde, evtl muss in einer Statistik das Schiff durchgestrichen werden.
-Ob das Schiff zerstört wurde kann man an der Klasse Schiff prüfen, die Schiffe speichern in ihrem Array ja ihre Felder (oder wenigstens ihre Beschussinfos).

Irgendwann hat man so alles im Kopf durchgeplant, weiß annähernd welche Klassen man benötigt und fängt an mit den ersten Codezeilen.
Man ist sich sicher, dass alles funktioniert und beim Programmieren wirst du dann auf weitere Probleme stoßen: Vielleicht blockiert deine GUI durch irgend eine Abfrage/Endlosschleife, oder irgendwo fliegt eine OutOfBounds-Exception. Oder der Spieler gewinnt nicht ... etc etc etc.
Dann kommt das ReDesign, Klassen werden geändert und vielleicht verwirft man ja auch alles und fängt mit dem neu gewonnenem Wissen neu an.

Hoffe ich konnte dir helfen 
Gruß


----------



## sunshine (27. Mrz 2011)

Mein Kopf raucht schon. Ich habe bis jetzt folgendes. Alles noch in einer Klasse aber kann mal jemand drüberschauen und sagen ob das so ok ist? 

```
public class Spielfeld {
	public static Status spielfeld [][]=new Status[10][10];
	
	public static void neuesSpielfeld(){
		for (int i = 0; i < spielfeld.length; i++) {
			for (int j = 0; j < spielfeld.length; j++) {
				spielfeld[i][j]=Status.WASSER_FREI;
			}
		}
	}
	

	public static boolean kannSetzen(int typ, int xkor, int ykor, int richtung){
		//Diesen Teil hab ich zwar schon ist aber 30Zeilen lang und funktioniert nicht Richtig
	}
	
	
	public static void setzeSchiff (int typ, int xkor, int ykor, int richtung){
		if(richtung==1){
			for (int i = xkor; i < xkor+typ; i++) {
				spielfeld[i][ykor]=Status.SCHIFF_FREI;
			}
		}
		else{
			for (int i = ykor; i < ykor+typ; i++) {
				spielfeld[xkor][i]=Status.SCHIFF_FREI;
			}
		}
		
	}
	 public static void Spielfeldausgabe(){
			int zeile=1;
			System.out.println("    A  B  C  D  E  F  G  H  I  J");
			for (int i = 0; i < spielfeld.length; i++) {
				if(zeile==10){
					 System.out.print(""+zeile+" ");
				}
				else
					System.out.print(""+zeile+"  ");
				zeile++;
				for (int j = 0; j < spielfeld.length; j++) {
					System.out.print(spielfeld[i][j]);
				}
				System.out.println("");
			}
			}
	 public static int xKor(String c){
		 int x = -1;
            if(c.equals("A"))
                      x=0; 
            else if(c.equals("B"))
                      x=1; 
            else if(c.equals("C"))
                      x=2;
            else if(c.equals("D"))
                      x=3; 
            else if(c.equals("E"))
                      x=4; 
            else if(c.equals("F"))
                      x=5; 
            else if(c.equals("G"))
                      x=6; 
            else if(c.equals("H"))
                      x=7; 
            else if(c.equals("I"))
                      x=8; 
            else if(c.equals("J"))
                      x=9; 
            else
                      System.out.println("Kein Passender Buchstabe eingegeben");
             return x;
       }

	 public static void setzeSchiffSpielfeld(int a){
		 String xkoordinate;
			int ykoordinate;
			boolean test=true;
			while(test){
			int x=-1;
			int y=-1;
			int richtung=-1;
			System.out.println("Setzen Sie ihr "+a+"er Schiff und geben Sie hierfür zuerst die Anfangskoodinate ein:");
			while(x==-1){
			System.out.println("Buchstabe:");
			xkoordinate=Input.in.readString();
			x = xKor(xkoordinate);
			}
			while(y==-1){
			System.out.println("Zahl:");
			ykoordinate= Input.in.readInteger();
			if(ykoordinate>=1&&ykoordinate<=10)
			    y=ykoordinate-1;
			else
				System.out.println("Keine gültige Zahle eingegeben");
			}
			while(richtung!=0&&richtung!=1){	
			System.out.println("In welche Richtung wollen sie ihr Schiff bauen? Vertikal(=0) oder Horizontal(=1)");
			richtung =Input.in.readInteger();
			}
			if(kannSetzen(a,x,y,richtung)==true){
				setzeSchiff(a,x,y,richtung);
				test=false;
			}
			else 
				System.err.println("Schiff kann nicht gesetzt werden!");
			}
	 }
	 
	public static void main(String[]args){
		neuesSpielfeld();
		setzeSchiffSpielfeld(5);
		Spielfeldausgabe();
		for (int i = 0; i < 2; i++) {
			setzeSchiffSpielfeld(4);
		}
		Spielfeldausgabe();
		for (int i = 0; i < 3; i++) {
			setzeSchiffSpielfeld(3);
		}
		Spielfeldausgabe();
		for (int i = 0; i < 4; i++) {
			setzeSchiffSpielfeld(2);
		}
		Spielfeldausgabe();
	}

}
```


----------



## sunshine (27. Mrz 2011)

Ist es möglich dass ich die Aufgabe erst so programmier dass das Spielfeld ganz stupide auf der Console ausgegeben wird und dann eine richtige darstellung Einbau?Oder ist das etwas umständlich?


----------



## Simon_Flagg (27. Mrz 2011)

das ist wieder das mit der trennung von ausgabe und dem unter der haube.... wenn du die trennung wirklich ordentlich hast, dann bräuchtest du einfach nur die grafik ausgabe für die console zu programmieren.... und wenn du fertig bist, durch einen frame ersetzen...

lg


----------



## kirax (27. Mrz 2011)

Mit den static Methoden machst du wieder genau das Gegenteil von OOP 
Ein Spielfeld ist auf jeden Fall ein Objekt, das auch in der Realität existiert, lässt sich also wunderbar als Objekt in Java realisieren.
Du entziehst dir aber mit den static Methoden die Möglichkeit, überhaupt Objekte zu erstellen.



> ```
> public static void neuesSpielfeld() {
> for (int i = 0; i < spielfeld.length; i++) {
> for (int j = 0; j < spielfeld.length; j++) {
> ...


Genau so würde der Konstruktor aussehen. Aber diese static Methode verpflichtet dich, sie vor jedem Spiel auszuführen. [c]Spielfeld feld = new Spielfeld(10, 10);[/c] würde mit folgendem Konstruktor das gleiche erledigen, und du bist nicht drauf angewiesen, nochmal eine extra Methode auszuführen (und mit der Möglichkeit der Übergabe der Länge und der Breite bist du sogar flexibel, was das angeht):


```
public class Spielfeld {
  private final int[][] feld;

  public Spielfeld(int laenge, int breite) {
    feld = new int[laenge][breite];
    for (int i = 0; i < laenge; i++) {
      for (int j = 0; j < breite; j++) {
        spielfeld[i][j]=Status.WASSER_FREI;
      }
    }
  }
  // ...
}
```

Das static zu machen ist zwar easy going, aber langfristig tust du dir damit keinen Gefallen.


----------



## sunshine (28. Mrz 2011)

Ok ich hab das Programm jetzt erstmal soweit, dass es auf der Konsole läuft.
Jetzt hab ich eine Methode die auf der Konsole nachfragt, welche Zeile und Spalte man setzen will bzw auf welche Zeile und Spalte man zielen will.

```
do {
			System.out.print("Please enter a " + prompt);

			System.out.print(" between 0 and " + (searchSpace - 1) + ": ");
			if(input.hasNextInt()){
			moveIndex = input.nextInt();
			}
			else
				System.out.println("Try Again!");
		} while (moveIndex >= searchSpace);
		return moveIndex;
	}
```
Jetzt ist mir das Programm immer abgestürzt, wenn ich zB einen Buchstaben eingegebem habe.
Das versuche ich jetzt abzufangen indem ich erst kläre, ob das eingegebene Zeichen auch ein Integerwert ist. Wenn ich das aber so mache habe ich eine endlos Schleife.


----------



## Firephoenix (28. Mrz 2011)

Hi,
wäre der Ansatz hier eine passende Änderung?

```
boolean correctChose = false;
		do {
			System.out.print("Please enter a " + prompt);

			System.out.print(" between 0 and " + (searchSpace - 1) + ": ");
			if(input.hasNextInt()){
				moveIndex = input.nextInt();
				if(moveIndex == gültigerWert)
					correctChose = true;
			}
			else
				System.out.println("Try Again!");
		} while (!correctChose);
		return moveIndex;
```
Gruß


----------



## sunshine (28. Mrz 2011)

Wo kommt "gültigerWert" her?


----------



## Firephoenix (28. Mrz 2011)

Hi,
ich wusste nicht wie du dein Feld aufgebaut hast, bzw wie moveIndex genau arbeitet, daher dachte ich es macht vielleicht Sinn eine überprüfung einzubauen ^^
Wenn dein Feld z.b. von 0-9 geht sollte an der stelle geprüft werden ob der benutzer irgendwas in dem zahlenbereich abschießen will, nicht dass dir am ende der benutzer auf -1 oder 1.000.000 schießt und nicht das schiff sondern dein array explodiert 
Es ist also kein gültiger Wert o.ä. sondern soll eher als Platzhalter dienen ^^
Gruß


----------



## sunshine (28. Mrz 2011)

Ok, aber bleibt eine endlos Schleife. Diesen Teil frag ich ja im Prinzip am Ende meiner do-while schleife ab mit moveIndex>=...


----------



## Firephoenix (28. Mrz 2011)

Hi,
Eine Endlosschleife macht ja auch Sinn, der User soll ja auf alle Fälle etwas eingeben.
Du musst nur dafür sorgen, dass bei einer korrekten Eingabe die Schleife verlassen werden kann (entweder über die zu prüfende Variable oder über ein break)
Gruß


----------



## dehlen (28. Mrz 2011)

EDIT;


----------



## sunshine (28. Mrz 2011)

Ja das die Schleife solang laufen soll bis ich was richtiges eingeb ist mir klar, aber im Moment kommt folgendes auf der Konsole wenn ich einen Buchstaben eingebe:
Please enter a row between 0 and 9: Try Again!
Please enter a row between 0 and 9: Try Again!
Please enter a row between 0 and 9: Try Again!
Please enter a row between 0 and 9: Try Again!
Please enter a row between 0 and 9: Try Again!
Please enter a row between 0 and 9: Try Again!
Please enter a row between 0 and 9: Try Again!
Please enter a row between 0 and 9: Try Again! usw.

Also ich bekomme gar keine Gelegenheit was anderes einzugeben.


----------



## Firephoenix (28. Mrz 2011)

Hi,
hättest du ein kompilierbares Beispiel, das du posten kannst?
Gruß


----------

