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 den Spielern die Arbeit abnimmt.
Ein Spiel-Objekt muss dazu natürlich die Figuren f1..f4 kennen und wissen,
welches die aktuellen Figuren sind.
Wenn gerade die Figuren f2
und f4
die aktuellen Figuren
sind, sieht ein passendes Objektdiagramm so aus:
Aufgabe 1 - String
String
ist prinzipiell eine Klasse wie jede andere (mit ein paar Besonderheiten in Java).
Um das Objektdiagramm übersichtlich zu halten, wurden Referenzen auf String
-Objekte
oben weggelassen. Ergänze die Referenzen auf die String
-Objekte.
Drucke dazu die Grafik des Objekdiagramms
aus, oder nutze Software, um auf den Bildschirm zu zeichnen.
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
bis f4
,
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:
Die Figur
-Klasse bleibt wie gehabt bestehen.
Die Spiel
-Klasse erhält vier Referenzattribute für die vier Spielfiguren
und zwei Referenzattribute für die aktuellen Figuren.
Außerdem sollen die beiden möglichen Farben, sowie die Farbe des aktuellen Spielers
gespeichert werden.
Auch hier handelt es sich um Referenzen auf Objekte. Diese sollen aber im Klassendiagramm
nicht explizit dargestellt werden.
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. die Position der Figuren oder die Farbe des aktuellen Spielers 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:
- aktuelleFigurenAuswuerfeln: 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 beiden Farben festgelegt werden und vier Figuren erzeugt werden.
Spielzustand ausgeben
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.
druckeGewinner
Um den Gewinner auszugeben, werden folgende Ideen als Struktogramm bzw. Flussdiagramm angeboten:
Aufgabe 4 - druckeGewinner
Implementiere die drei Varianten der Methode. Welche favorisierst Du? (Tipp: Eine der dargestellten Möglichkeiten ist in sehr seltenen Fällen nicht korrekt, nämlich dann wenn zwei Spieler gleichzeitig auf das Home-Feld kommen.) Wünschenswert wäre auch eine Ausgabe der Art: "blau hat gewonnen" statt "Figur 1 hat gewonnen".
druckeAktuellenSpieler
Die Ausgabe des aktuellen Spielers ist zunächst sehr leicht.
Aufgabe 5 - druckeAktuellenSpieler - Version 1
Teste die Methode und erkläre die Ausgabe.
Aufgabe 6 - druckeAktuellenSpieler - Version 2
Teste die veränderte Version der Methode.
druckeAktuelleFiguren
Ähnlich lässt sich die Ausgabe der Positionen der aktuellen Figuren erledigen:
Aufgabe 7 - druckeAktuelleFiguren
Teste die Methode. Erkläre den Fehler und behebe ihn.
druckeSpielfeld
Schließlich wäre es noch sehr hilfreich eine Übersicht über das gesamte Spielfeld zu erhalten.
Aufgabe 8 - druckeSpielfeld
Die Methode zur übersichtlichen Ausgabe des ganzen Spielfeldes ist recht komplex. Diese musst Du nicht verstehen und kannst sie einfach in Dein Programm kopieren. Das funktioniert natürlich nur, wenn Deine Variablennamen mit den oben beschriebenen übereinstimmen.
void druckeSpielfeld()
{
StringBuilder[][] feld = new StringBuilder[5][5];
for(int x = 0; x < 5; x++)
for(int y = 0; y < 5; y++)
feld[x][y] = new StringBuilder();
if(f1 == aktuelleFigur1 || f1 == aktuelleFigur2)
feld[f1.x+2][f1.y+2].append(farbe1.toUpperCase().charAt(0));
else
feld[f1.x+2][f1.y+2].append(farbe1.toLowerCase().charAt(0));
if(f2 == aktuelleFigur1 || f2 == aktuelleFigur2)
feld[f2.x+2][f2.y+2].append(farbe1.toUpperCase().charAt(0));
else
feld[f2.x+2][f2.y+2].append(farbe1.toLowerCase().charAt(0));
if(f3 == aktuelleFigur1 || f3 == aktuelleFigur2)
feld[f3.x+2][f3.y+2].append(farbe2.toUpperCase().charAt(0));
else
feld[f3.x+2][f3.y+2].append(farbe2.toLowerCase().charAt(0));
if(f4 == aktuelleFigur1 || f4 == aktuelleFigur2)
feld[f4.x+2][f4.y+2].append(farbe2.toUpperCase().charAt(0));
else
feld[f4.x+2][f4.y+2].append (farbe2.toLowerCase().charAt(0));
if(feld[2][2].toString().equals(""))
feld[2][2] = new StringBuilder("HOME");
System.out.print(" ------------------------");
for(int y = 2; y >= -2; y--)
{
System.out.printf("\n%2s |", y);
for(int x = -2; x <= 2; x++)
{
System.out.printf("%-4s|", feld[x+2][y+2]);
}
System.out.print("\n ------------------------");
}
System.out.println("\n -2 -1 0 1 2");
}
Methoden zur Spieldurchführung
Bisher haben wir das Grundgerüst der Klasse erzeugt und sinnvolle 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.
aktuelleFigurenAuswuerfeln
Vor jedem Zug muss gewürfelt werden. Es muss also dafür gesorgt werden, dass die aktuelleFigur1 und die aktuelleFigur2 jeweils zufällig auf eine der Figuren f1 bis f4 verweist.
Aufgabe 9 - aktuelleFigurenAuswuerfeln
Setze die im Struktogramm dargestellte Methode um.
nach...Bewegen
Die Spieler sollen sich keine Gedanken über die Codierung der Richtung machen müssen. Deshalb werden entsprechende Methoden bereit gestellt, welche die beiden aktuellen Figuren in die gewünschte Richtung bewegen.
Aufgabe 10 - nach...Bewegen
Setze die oben angedeutete Methode für alle vier Richtungen um.
void nachObenBewegen()
{
aktuelleFigur1.gehe(0);
aktuelleFigur2.gehe(0);
}
setzeNeuenSpieler
Die Methode setzeNeuenSpieler hat die Aufgabe die aktuelle Spielfarbe zu tauschen. Wenn die Farbe des ersten Spielers aktuell ist, soll nach Methodenaufruf die Farbe des zweiten Spielers aktuell sein und umgekehrt.
Aufgabe 11 - setzeNeuenSpieler
Du erkennst vielleicht direkt das Problem des obigen Diagramms. Falls nicht, kannst Du die Methode wie oben dargestellt umsetzen und die Methode ein paar mal ausführen. Beschreibe das Problem und implementiere eine verbesserte Version. (Tipp: Welchen Wert hat aktuelleFarbe 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 oder f3 oder f4 gewonnen hat.
Aufgabe 13 - neuenZugVorbereiten
Die erforderlichen Aktionen, um einen neuen Zug vorzubereiten, lassen sich leicht automatisieren.
Aufgabe 14 - Testen des Spiels
Play the game :-)
(Optional kannst Du noch die nach außen nicht benötigten Methoden durch den Zugriffsmodifikator
private
"verstecken".)