Bearbeitete Aufgabe: Schiffe versenken
von: Daniel Hakenjos und Sebastian Nusser
Lösungsidee
Zuerst sollte es dem Spieler möglich sein, das Spiel zu starten und zu beenden. Dies wird realisiert über die Button "Stop" und "Start". Der Computer soll daraufhin seine Schiffe per Zufall auf dem Spielfeld verteilen. Danach setzt der Spieler per Mausklick seine Schiffe auf ein entsprechendes Feld seines Spielfeldes. Wenn die linke Maustaste gedrückt wird, soll das Schiff in vertikaler Richtung plaziert werden; wenn man die rechte Maustaste drückt, wird das Schiff in horizontaler Ausrichtung gesetzt. Wobei das angeklickte Feld den Bug des Schiffes darstellt. Die Schiffe werden ihrer Länge nach gesetzt (d.h. zuerst Zweier, Dreier...).
Der Rechner bestimmt per Zufall wer anfängt und dann beginnt auch schon die Schlacht! Man klickt auf ein Feld, wo man ein Schiff vermutet. Doppelte Schüsse auf ein Feld werden abgewiesen. Ob getroffen wurde oder nicht, zeigt ein blauer Punkt (es wurde nur Wasser getroffen) oder ein roter Punkt (ein Schiff wurde getroffen) an. Gleichzeitig steht neben den Spielfeldern die Flotte der Spieler und wenn ein Schiff versenkt wurde, wird dieses aus dieser Flotte entfernt. Wenn ich als Spieler eines versenkt habe, nehme ich es auch im Spielfeld des Gegners war, denn das versenkte Schiff wird gezeichnet.
Computer und Spieler wechseln sich ab, bis einer gewonnen hat, oder der Spieler das Spiel selbst unterbricht! Alle wichtigen Aktionen werden zusätzlich in einer Art Statusanzeige ausgegeben. Um das ganze abzurunden hat man auch die Möglichkeit sich eine Hilfe anzuschauen. Um dem Spieler zu zeigen welches sein Spielfeld bzw. Flotte muss er beim Initialisieren des Applets seinen Namen eingeben.
Für die Hintergrunddarstellung bzw. Darstellung haben wir jpg-Bilder genutzt, deren Speicheraufwand nicht groß ist und somit auch für ein Applet genutzt werden kann, ohne lange Ladezeiten zu haben.
Datenstrukturen und Algorithmen
Wenn das Applet initialisiert wird, erscheint ein Fenster mit super: "Herzlich Willkommen". Dieses Frame ist als Objekt der Klasse first.class erzeugt wurden. Der Spieler muss in einem TextField sein Namen eingeben. Dies dient dazu dem Spieler sichtbar zu machen, welches Spielfeld und welche Flotte ihm gehört. Erst wen er das Fenster durch "Bestätigen" verlassen hat, kann er das Spiel starten oder sich die Hilfe anschauen. Die Hilfe wurde ebenfalls durch eine Klasse HelpFrame.class realisiert. Ein Objekt dieser Klasse zeigt dem Benutzer ein Fenster, in dem einige Informationen über Sinn und Zweck bzw. Hinweise zum Spielen findet. Wenn der Benutzer "Start" drückt, wird ein Objekt der Klasse Play.class erzeugt, welches die Java-Klasse erweitert. Somit läuft das Spiel als Thread und es ist die Interaktion mit der Maus möglich bzw. der Spieler kann auch das Spiel stoppen.
Die gesamte Grafik wird in der Klasse PlayGround.class behandelt. Ein Objekt dieser Klasse wird bereits in der Appletklasse BattleShipApp.class erzeugt, denn PlayGround enthält weiterhin den MouseAdapter, über den die Mausaktionen (u.a. Maus gedrückt) gesteuert werden.
Die Klasse PlayGround.class stellt Funktionen zur Verfügung, mit denen zum einen die Spielfläche - frei plazier- und skalierbar, mit variabler Feldanzahl - gezeichnet (drawPlayGround(startX, startY, endeX, endeY, xFelder, yFelder)), zum anderen werden die Schiffe auf der Spielfläche dargestellt (drawComputerShipX(typ, posX, posY), drawComputerShipY(typ, posX, posY), drawPlayerShipX(typ, posX, posY), drawPlayerShipY(typ, posX, posY)) und die "Schüsse" auf der Spielfläche (drawShotWaterComputer(posX, posY), drawShotShipComputer(posX, posY), drawShotWaterPlayer(posX, posY), drawShotShipPlayer(posX, posY)) visualisiert.
Die Funktion MouseClicked(event) bestimmt die Position eines Mausereignisses auf der Spielfläche und erzeugt der relative Mausposition, die durch drawPlayGround(startX, startY, endeX, endeY, xFelder, yFelder)) festgelegt wird, entsprechend ein Reaktion des Programms, indem Funktionen des Player-Objekts aufgerufen werden.
Die Klasse BattleShip.class symbolisiert als Datentyp ein einzelnes Spielfeld, d.h. für die beiden Spieler des Spieles werden zwei Objekte erzeugt. Sie stellt zwei Funktionen (placeX(posX, posY) und placeY(posX, posY)) zur Verfügung, mit der die Schiffe gesetzt werden. Diese Funktionen liefern Boolean-Werte zurück, damit andere Programmteile erkennen können, ob die einzelnen Schiffe tatsächlich auch gesetzt wurden. Die Funktion hit(posX, posY) liefert den Typ eines Treffers (-1: bereits auf Feld geschossen; 0: Wasser getroffen; 1-5: einzelner Schiffstyp) und markiert das entsprechenden Feld als "Schuss" - sunk(typ) liefert true zurück, wenn das Schiff des angegebenen Typs tatsächlich versenkt wurde.
Mit den Funktionen getStartPosOfShipX(typ) und getStartPosOfShipY(typ) liefern die Startposition der Schiffe zurück. Dies wird für die Anzeige in PlayGround.class benötigt, um die vom Computer gesetzten Schiffe darstellen zu können. Die Funktion isDirectedOnX(typ) bzw. isDirectedOnY(typ) liefert als Boolean-Wert die Orientierung eines Schifftyps zurück.
Intern werden die Schiffe in einen Integer-Array abgelegt, dies ermöglicht die Unterscheidung der einzelnen Schiffstypen in den einzelnen Funktionen. Zusätzlich wird noch ein zweiter Array vom Typ boolean erzeugt; dieser stellt die Kontrolle dar, ob schon auf ein Feld "geschossen" wurde oder nicht; ein Integer-Array in dem die Treffer auf ein Schiffstyp und ein weiteres Integer-Array, das den Schiffstypen ihren Standardlänge zuordnet.
Weiterhin haben wir eine Klasse Player.class geschrieben. Diese Klasse hat verschiedene Attribute, in denen die Schußanzahl, Anzahl der Treffer, versenkte Schiffe, Namen des Players und Prädikat ob gewonnen gespeichert werden. Von dieser Klasse werden wieder zwei Objekte erzeugt (Computer und Spieler). Wenn sich während des Spielstandes etwas verändert, wird es in dem jeweiligen Objekt geändert und kann dann sichtbar werden.
Testdaten
interner Testrahmen der BattleShip.class (exemplarisch):
setzen der Schiffe (mit Kontrolle ob möglich):
x-Position, y-Position,
Start, y-Laenge, y-Laenge belegt, kein Fehler
Schiff auf x=5,y=0: true
x-Position, y-Position, Start, x-Laenge, x-Laenge belegt, kein Fehler
Schiff auf x=0,y=2: true
x-Position, y-Position, Start, x-Laenge, x-Laenge belegt, kein Fehler
Schiff auf x=0,y=0: true
"Schuss" auf ein bestimmtes Feld:
Schuss auf x=5,y=0: 2
Schuss auf x=0,y=2: 5
Schuss auf x=1,y=2: 5
Frage ob letzter Treffer ein Schiff versenkt hat:
Versenkt von 5: false
Ausgabe zur Kontrolle (Schiff-Array):
4 4 4 4 0 2
0 0 0 0 0 2
5 5 5 5 5 2
Ausgabe zur Kontrolle (Treffer-Array):
false false false false
false true
false false false false false false
true true false false false false
Abfrage der Treffer pro Schiffstyp:
Anzahl Treffer fuer 1:
0
Anzahl Treffer fuer 2: 1
Anzahl Treffer fuer 3: 0
Anzahl Treffer fuer 4: 0
Anzahl Treffer fuer 5: 2