Go Home als Programm
Ein Spiel-Objekt
Das Spiel im Codepad zu spielen ist zwar möglich, aber sehr mühsam und macht so nicht viel Spaß. Wir benötigen also ein Objekt, das uns die Steuerung des Spiels erleichtert. Ein Spiel-Objekt muss dazu natürlich die Figuren f1 und f2 kennen und wissen, welches die aktuellen Figuren sind, die gezogen werden sollen. Außerdem sollte gespeichert werden welcher Spieler gerade an der Reihe ist. Der Spieler wird durch seine Figur repräsentiert. Ein Objektdiagramm könnte also so aussehen:
Aufgabe 1
Analyisiere das Objektdiagramm und beschreibe den aktuellen Spielzustand. (Welcher Spieler ist am Zug? Welche Figuren werden bewegt? In welche Richtung könnte der Spieler die Figuren bewegen?)
Eine Klasse für die Spiele-Logik
Die Beziehungen zwischen den Klassen Spiel
und Figur
kann und sollte
man auch im Klassendiagramm ausdrücken.
Man unterscheidet dabei kennt-Beziehungen und
hat-Beziehungen.
Bei einer kennt-Beziehung kennt ein Objekt ein anderes Objekt, beide sind aber unabhängig voneinander
existenzfähig.
Bei einer hat-Beziehung ist das besitzende Objekt meist auch für die Erzeugung der anderen
Objekte zuständig.
Im obigen Fall existieren hat-Beziehungen auf die Figuren f1
und f2
,
da das Spiel auch für die Erzeugung der Figuren zuständig ist und diese ein Teil des Spiels sind.
Es existieren aber auch kennt-Beziehungen, da die aktuellen Spielfiguren ebenfalls
bekannt sein müssen, aber nicht noch einmal erzeugt werden.
Ob eine Beziehung eine kennt- oder hat-Beziehung ist, ist nicht immer eindeutig.
Man drückt die Beziehungen im Klassendiagramm folgendermaßen aus:
In beiden Fällen äußert sich die Beziehung darin, dass ein Attribut benötigt wird, um die Referenz auf die anderen Objekte zu speichern.
Um auszudrücken wie diese Attribute benannt sein sollen, kann man die Enden der Beziehungspfeile auch beschriften. Die folgenden Klassendiagramme haben also eine ähnliche Aussagekraft:
bzw.
Aufgabe 2 - Beziehungen in Klassendiagrammen
Beschreibe die Unterschiede der drei dargestellten Klassendiagramme und bewerte die Stärken und Schwächen der unterschiedlichen Darstellungen.
Klassendiagramm
Insgesamt ergibt sich damit z.B. dieses Klassendiagramm (kein UML sondern Java-spezifisch):
Die Figur
-Klasse bleibt wie gehabt bestehen.
Die Spiel
-Klasse erhält mehrere Referenzattribute für die Spielfiguren.
Im Klassendiagramm sind diese durch die Pfeile dargestellt.
Im Konstruktor wird das Spiel initalisiert, also z.B. die Farben festgelegt und die Figuren erzeugt.
Um den aktuellen Zustand des Spiels auszugeben, soll es verschiedene Methoden geben, die z.B. den aktuell ziehenden Spieler, die zu bewegenden Figuren und das Spielfeld ausgeben.
Für den Benutzer am einfachsten ist es, wenn er sich nicht um die Koordinaten kümmern muss, um die Figuren zu bewegen. Deshalb gibt es vier verschiedene Methoden für die Bewegungen nach oben, rechts, unten und links.
Schließlich gibt es noch mehrere Methoden, die für die Steuerung des Spiels zuständig sind:
- muenzenWerfen: Sorgt dafür, dass zwei zufällige Figuren bestimmt werden, die dann im nächsten Zug bewegt werden müssen.
- spielIstFertig:Überprüft, ob einer der Spieler auf dem Home-Feld ist.
- setzeNeuenSpieler:Wechselt den aktuellen Spieler.
- neuenZugVorbereiten: Erledigt alles, was getan werden sollte, nachdem ein Spieler gezogen hat.
Grundgerüst
Zuerst müssen wir das Grundgerüst für die Klasse Spiel
erstellen. Dazu gehört, dass wir die Attribute und den Konstruktor programmieren.
Aufgabe 3 - Grundgerüst implementieren
Erstelle eine neue Klasse Spiel
in BlueJ.
Implementiere die Attribute der Klasse und den Konstruktor.
Im Konstruktor sollen die Figuren mit entsprechenden Farbbezeichnungen
und Koordinaten erzeugt werden.
Hilfe
class Spiel
{
Figur f1;
...
Figur aktuelleFigur1;
...
Spiel()
{
f1 = new Figur(...);
...
}
}
druckeGewinner
Damit man das Spiel komfortabel spielen kann, muss natürlich eine übersichtliche
Ausgabe des Spielzustands erfolgen.
Dies erfolgt in unterschiedlichen Methoden, um flexibler zu sein und den Code
übersichtlich zu halten.
Wir beginnen mit der Methode druckeGewinner
.
Um den Gewinner auszugeben, kann man folgendermaßen vorgehen:
Aufgabe 4
Implementiere die Methode druckeGewinner
, so dass eine
Ausgabe in der Art "Blau hat gewonnen!" oder "Rot hat gewonnen!" erfolgt,
wenn es einen Gewinner gibt.
Du kannst auch noch den Fall ergänzen, dass beide gleichzeitig auf dem Home-Feld angelangen.
druckeAktuellenSpieler
Die Ausgabe des aktuellen Spielers scheint zunächst sehr leicht.
void druckeAktuellenSpieler() {
System.out.println("Am Zug ist " + aktuellerSpieler.farbe);
}
Allerdings funktioniert die Methode nicht wie erwartet, wenn der aktuelle Spieler noch nicht festgelegt wurde. Deshalb musst du die folgende Version der Methode verwenden:
void druckeAktuellenSpieler() {
if(aktuellerSpieler != null) {
System.out.println("Am Zug ist " + aktuellerSpieler.farbe);
}
}
Aufgabe 5
Teste beide Versionen der Methode druckeAktuellenSpieler
und erkläre den Unterschied.
Gehe insbesondere auf die Bedeutung des Schlüsselwortes null
ein.
druckeAktuelleFiguren
Ähnlich lässt sich die Anzeige der aktuell zu bewegenden Figuren erledigen:
private void druckeAktuelleFiguren() {
System.out.println("Bewege " + aktuelleFigur1.farbe + " und " + aktuelleFigur2.farbe);
}
Aufgabe 7
Teste die Methode. Erkläre den Fehler und behebe ihn.
druckeSpielfeld
Schließlich benötigen wir noch eine Übersicht über das Spielfeld in der folgenden Art:
. . . . . . . . B . R . X . . . . . . . . . . . .
Das Struktogramm verdeutlicht die Implementierungsidee:
Implementieren lässt sich die Methode auf folgende Art:
void druckeSpielfeld() {
for(int y = 0; y <= 4; y++) {
for(...) {
if (x == 2 && y == 2 && !spielIstFertig())
System.out.print("X ");
else if (f1.x == x && f1.y == y && f2.x == x && f2.y == y)
System.out.print( ... );
else if( ... )
System.out.print("B ");
else if( ... )
System.out.print("R ");
else
System.out.print(". ");
}
System.out.println();
}
}
Aufgabe 8
Beschreibe die Funktionsweise der Methode und ergänze die Implementierung an den Stellen, an denen noch Platzhalter stehen.
muenzenWerfen
Bisher haben wir das Grundgerüst der Klasse erzeugt und Methoden geschrieben, um den aktuellen Spielzustand auszugeben. Spielen kann man das Spiel bis jetzt noch nicht. Das soll sich durch Implementierung der nächsten Methoden ändern.
Vor jedem Zug müssen zwei Münzen geworfen werden, um zu bestimmen, welche Figuren
bewegt werden sollen, welche Figuren also die
aktuelleFigur1
und
die aktuelleFigur2
sind.
Als Pseudocode könnte man das so formulieren:
Erzeuge zwei zufällige Zahlen zwischen 0 und 1 Wenn die erste Zahl 0 ist, dann ist aktuelleFigur1 f1, sonst f2 Wenn die zweite Zahl 0 ist, dann ist aktuelleFigur2 f1, sonst f2
Aufgabe 9
Implementiere die Methode muenzenWerfen.
nach...Bewegen
Wir benötigen nun noch Methoden, um die per Münzwurf ermittelten Figuren zu bewegen.
Aufgabe 10
Setze die entsprechenden Methoden für alle vier Richtungen um.
void nachObenBewegen() {
aktuelleFigur1.gehe(0);
aktuelleFigur2.gehe(0);
}
setzeNeuenSpieler
Die Methode setzeNeuenSpieler
hat die Aufgabe den
aktuellen Spieler zu tauschen.
Dazu wird folgende Methode vorgeschlagen:
private void setzeNeuenSpieler() {
if(aktuellerSpieler == f1)
aktuellerSpieler = f2;
else
aktuellerSpieler = f1;
}
Aufgabe 11
Du erkennst vielleicht direkt das Problem des obigen Diagramms bei Spielbeginn.
Falls nicht, kannst du die Methode wie oben dargestellt umsetzen und die Methode
ein paar mal an einem neu erzeugten Spiel ausführen.
Beschreibe das Problem und implementiere eine verbesserte Version.
(Tipp: Welchen Wert hat aktuellerSpieler
zu Beginn des Spiels?)
Spielverlauf automatisieren
Das Spiel ist nun im Prinzip fertig und spielbar. Wenn du möchtest, kannst du ein Spiel (evtl. auch teilweise) spielen. Du wirst merken, dass du im Prinzip immer wieder die gleichen Methoden aufrufst, oft auch in der gleichen Reihenfolge. Um den Komfort zu steigern, solltest du noch ein paar Ergänzungen am Spiel vornehmen.
Aufgabe 12 - spielIstFertig
Ergänze eine Methode, die feststellt, ob das Spiel fertig ist oder nicht.
Das Spiel ist fertig, wenn f1
oder f2
gewonnen hat.
Aufgabe 13 - neuenZugVorbereiten
Die erforderlichen Aktionen, um einen neuen Zug vorzubereiten, lassen sich leicht automatisieren.
spiel fertig? ja: gebe Gewinner aus nein: tausche Spieler gebe aktuellen Spieler aus münzen werfen, um aktuelle Figuren zu bestimmen gebe aktuelle Figuren aus drucke Spielfeld
Aufgabe 14 - Testen des Spiels
Spiele das Spiel :-)
Optional kannst du noch die nach außen nicht benötigten Methoden durch den
Zugriffsmodifikator private
"verstecken".