Logo des digitalen Schulbuchs inf-schule.de. Schriftzug in Zustandsübergangsdiagramm eines endlichen Automaten.

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: Objektdiagramm mit Spiel und vier Figuren

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:

Kennt- und hat-Beziehunge zwischen Spiel und Figur

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:

Beschriftete Beziehungen
bzw.
Referenzattribute explizit angegeben

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:

Klassendiagramm für Spiel

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:

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.

Hilfe zur Erstellung des Grundgerüsts

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:

PAP druckeGewinner

Struktogramm druckeGewinner Struktogramm druckeGewinner

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

druckeAktuellenSpieler ohne null-Abfrage

Teste die Methode und erkläre die Ausgabe.

Aufgabe 6 - druckeAktuellenSpieler - Version 2

Teste die veränderte Version der Methode.

druckeAktuellenSpieler mit null-Abfrage

druckeAktuelleFiguren

Ähnlich lässt sich die Ausgabe der Positionen der aktuellen Figuren erledigen:

druckeAktuelleFiguren

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.

aktuelleFigurenAuswuerfeln

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.

setzeNeuenSpieler

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.

Hilfe zur Methode neuenZugVorbereiten

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".)

X

Fehler melden

X

Suche