Basisversion des Spiels
Grundgerüst
Wir fangen zuerst mit einer ganz einfachen Version des Spiels an und werden diese dann Stück für Stück ergänzen und verbessern. Am Ende dieser Seite wird dann eine einfache, aber für zwei Spieler spielbare Version des Spiels entstanden sein. Diese solltest Du dann z.B. anhand der Übungen noch weiter ausbauen.
Als Ausgangspunkt für die Implementierung nutzen wir folgenden Code:
class Spiel
{
Spiel()
{
}
void rateEinmal()
{
System.out.println("Wie viel ist " + zufallszahl(10) + " + " + zufallszahl(10) + "?");
leseZahl();
System.out.println("Das Ergebnis war " + (zufallszahl(10) + zufallszahl(10)) );
}
int zufallszahl(int n)
{
return new java.util.Random().nextInt(n);
}
int leseZahl()
{
return new java.util.Scanner(System.in).nextInt();
}
}
Die Methode zufallszahl
gibt eine Zufallszahl von 0 bis n-1 zurück.
Da wir an mehreren Stellen Zufallszahlen erzeugen werden, ist es praktischer diese
Methode zu nutzen, anstatt immer den recht länglichen Ausdruck
new java.util.Random().nextInt(n)
zu notieren.
Benutzereingaben sind in Java etwas kompliziert, weswegen wir hier einfach die
angegebene Methode leseZahl
nutzen.
Du musst diese nicht verstehen. Du musst nur wissen, was die Methode
bewirkt: Auf dem Bildschirm erscheint eine Eingabemöglichkeit für den Benutzer, um
eine Zahl einzugeben. Die Methode hält so lange an, bis der Benutzer eine Zahl
eingegeben hat und gibt diese dann zurück.
Falls der Benutzer keine gültige Zahl eingibt, bricht das Programm mit einer Fehlermeldung ab.
Aufgabe 1 - Grundgerüst
- Erstelle in BlueJ ein neues Projekt und darin die Klasse
Spiel
. Kopiere den obigen Quellcode hinein und übersetze/compiliere die Klasse. Teste die beiden Methodenzufallszahl
undleseZahl
, um deren Wirkung kennenzulernen. - Teste und erkläre die Wirkung von
rateEinmal
. Achte dabei auch auf die unterschiedliche Wirkung des+
-Operators. Hinweis:System.out.println(...)
gibt den Wert des übergebenen Parameters auf dem Bildschirm aus.
Lokale Variablen
Damit das Programm funktioniert, benötigen wir eine Möglichkeit mindestens die
Zufallszahlen und evtl. noch weitere Daten zu speichern.
Theoretisch könnte man dies in Form von Attributen der Klasse Spiel
erreichen.
Allerdings handelt es sich um Variablen, die wir eigentlich nur während der
Ausführung der Methode rateEinmal
benötigen.
Es macht also viel mehr Sinn diese Variablen dort zu definieren, wo wir
sie auch benötigen, nämlich als lokale Variablen
in der Methode rateEinmal
.
Lokale Variablen werden innerhalb der Methode (genauer: ihres Geltungsbereichs) erzeugt und am Ende wieder zerstört. Wird die Methode ein zweites mal aufgerufen, sind die Werte des ersten Aufrufs also verloren. Außerdem muss man beachten, dass lokale Variablen in Java immer initialisiert (also mit einem Wert belegt, werden müssen), während Attribute automatisch mit einem Standardwert (z.B. 0 für alle Zahlentypen) belegt werden.
Aufgabe 2 - lokale Variablen
Ordne die Codeschnipsel, um eine funktionierende Version der Methode rateEinmal
zu erhalten. Ergänze die Methode in Deinem Programm und teste sie.
Fallunterscheidungen
Bis jetzt muss man selbst überprüfen, ob man richtig gerechnet hat. Viel schöner wäre natürlich, wenn der angezeigte Text dies in einer Fallunterscheidung schon berücksichtigt.
Fallunterscheidungen lassen sich unterschiedlich darstellen. Umgangssprachlich könnte man sagen: Falls die Antwort richtig eingegeben wurde, dann gebe Super! aus, sonst gebe Leider falsch aus.
Eine Fallunterscheidung enthält also eine Bedingung, Anweisungenen, die ausgeführt werden, falls die Bedingung wahr ist, und evtl. alternative Anweisungen.
Die Abläufe lassen sich auch durch ein Struktogramm oder Flussdiagramm visualisieren:
Aufgabe 3 - Fallunterscheidungen in Java
Sortiere die Codeschnipsel und ergänze die Methode rateEinmal
.
Beachte den Unterschied zwischen ==
und dem Dir bisher bekannten =
.
Zwei-Spieler-Version mit Punkten
Als nächstes sollen zwei Spieler gegeneinander spielen können und die Punkte gezählt werden.
Aufgabe 4 - rateEinmal mit Rückgabe
Um überhaupt Punkte zählen zu können, soll die Methode rateEinmal
zurückgeben, ob der Spieler richtig gerechnet hat, oder nicht.
Ändere die Methode so ab, dass zusätzlich zur Ausgabe true
zurückgegeben wird,
falls der Spieler richtig gerechnet hat, ansonsten false
.
Die Signatur der Methode muss so
geändert werden, dass ein Wahrheitswert zurückgegeben werden kann.
Am Endes des if-Zweiges muss true
zurückgegeben werden, am Ende des else-Zweiges muss
false
zurückgegeben werden.
Aufgabe 5 - eineRundeSpielen
Füge eine neue Methode eineRundeSpielen
ein gemäß den folgenden Anforderungen:
- Die Zahl der gespielten Runden soll gezählt werden.
Dazu wird ein Attribut
runden
benötigt, das innerhalb der MethodeeineRundeSpielen
erhöht wird. - Es soll zwei mal die Methode
rateEinmal
aufgerufen werden, um für die beiden Spieler je eine Aufgabe zu stellen. - Wenn der erste Spieler richtig geraten hat, soll dessen Punktestand
um eins erhöht werden. Dazu muss ein neues Attribut
punkte1
eingeführt werden. Für den zweiten Spieler gilt dies analog. - Der Punktestand der beiden Spieler soll ausgegeben werden.
Oben im Quelltext der Klasse müssen drei Attribute eingefügt werden mit
int punkte1;
,
int punkte2;
und
int runden;
.
Die Methode eineRundeSpielen
besteht aus den (sortierbaren) Zeilen:
Automatischer Spielablauf
Bis jetzt musst Du die Methode eineRundeSpielen
so oft
aufrufen wie Du spielen möchtest.
Statt dessen soll nun eine Spielelogik dafür sorgen, dass automatisch
fünf Runden gespielt und dann der Gewinner ausgegeben wird.
Aufgabe 6 - spielSteuern
Schreibe dazu eine neue Methode spielSteuern
.
Diese ruft die Methode eineRundeSpielen
auf, solange die Rundenzahl kleiner als fünf ist.
Danach wird überprüft wer gewonnen hat und eine entsprechende Meldung
ausgegeben. Hinweis: Besteht ein if- oder else-Zweig nur aus einer einzelnen
Anweisung können die geschweiften Klammern auch weggelassen werden.
Das kann den Code manchmal übersichtlicher machen.
Als Struktogramm lässt sich das folgendermaßen darstellen:
Wie man Wiederholungen programmiert, wirst Du im nächsten Projekt ausführlich lernen. Du wirst aber bestimmt keine Probleme haben, das vorgegebene Fragment zu verstehen und gemäß dem Struktogramm zu ergänzen:
void spielSteuern()
{
while(runden < 5) {
eineRundeSpielen();
}
// Hier musst Du noch überprüfen wer gewonnen hat...
}
Logische Operatoren
Die Gewinnregel soll nun etwas angepasst werden: Der Spieler, der zuerst fünf Punkte hat, gewinnt. Es sollen aber höchstens zehn Runden gespielt werden.
Wir benötigen dazu die Möglichkeit einzelne Wahrheitswerte zu verknüpfen oder zu negieren,
also deren Wert umzukehren. In Java benutzt man den Operator &&
,
um eine logisches "Und" auszudrücken.
Der Operator ||
drückt ein logisches "Oder"
aus. Der Operator !
stellt ein logisches "Nicht"
dar, kehrt also einen Wahrheitswert um.
Aufgabe 7 - Spielende
Welche der dargestellten Ausdrücke passen, um die bisherige Bedingung
runden < 5
in der Methode spielSteuern
gemäß der oben formulierten Gewinnregel zu ersetzen?
runden < 10 && !(punkte1 >= 5 && punkte2 >= 5)
(runden < 10 && !(punkte1 >= 5)) && (runden < 10 && !(punkte2 >= 5))
(runden < 10 && !(punkte1 >= 5)) || (runden < 10 && !(punkte2 >= 5))
runden < 10 && !(punkte1 >= 5 || punkte2 >= 5)
runden < 10 && punkte1 < 5 && punkte2 < 5
Passe Dein Programm entsprechend an und teste das Spiel.
Aufgabe 8 - Erste Anpassungen
Das Spiel ist nun in der ersten Version fertig und spielbar. Ändere erste Details, indem Du z.B. die Ausgabe Deinen Wünschen anpasst oder den Zahlenbereich der Aufgaben änderst.
Optional: Überlege, welche Elemente public
oder private
sein sollten und
ändere den Code entsprechend ab.