Wiedereinstieg: Würfeln bis 100 #
Das Spiel kennenlernen #
Sieh’ dir die folgenden Klassen an. Versuche, den Sinn jeder Methode zu verstehen, bevor du die Kommentare liest.
class Würfel {
int seiten;
Würfel(int seiten) {
this.seiten = seiten;
}
int würfle() {
return Random.randint(1, this.seiten);
}
}
- Offensichtlich möchte hier jemand Würfel-Objekte erzeugen.
class Würfel
eröffnet die Beschreibung dieser Objekte. (Erzeugt werden sie dann, mit dieser Klasse als Bauplan, woanders.) int seiten
deklariert ein Attribut (vermutlich die Anzahl der Seiten eines nach dieser Klasse erzeugten Würfel-Objekts). Ein anderer Name für Attribut ist Instanzvariable.- Die Methode
Würfel
ist die Konstruktor-Methode. MitWürfel(int seiten)
nimmt sie schon bei der Erzeugung eines neuen Würfel-Objekts die Anzahl der Seiten entgegen, und speichert diese mitthis.seiten = seiten
in der Instanzvariableseiten
ab. Der Konstruktor hat keinereturn
-Anweisung, gibt aber ein Objekt der KlasseWürfel
zurück. - Die Klasse
Würfel
legt eine einzige Methode (neben dem Konstruktor) für Würfel-Objekte fest, nämlich mitwürfle
das Würfeln. Die Methode ist sehr kurz, sie gibt (mit derreturn
-Anweisung) eine Zufallszahl zwischen 1 und der Anzahl der Seiten des Würfels zurück.
class Spielfigur {
String farbe;
int punkte;
Würfel würfel;
Spielfigur(String farbe) {
this.farbe = farbe;
this.punkte = 0;
}
void zeigePunkte() {
println("Ich habe " + this.punkte + " Punkte.", farbe);
}
int gibPunkte() {
return this.punkte;
}
void erhalteWürfel(Würfel w) {
this.würfel = w;
}
Würfel gibWürfel() {
Würfel w = this.würfel;
this.würfel = null;
return w;
}
void würfle() {
this.punkte = this.punkte + this.würfel.würfle();
}
boolean hatGewonnen() {
boolean gewonnen = this.punkte > 100;
if(gewonnen) {
println("Ich habe gewonnen!", this.farbe);
}
return gewonnen;
}
}
- Du solltest jetzt in der Lage sein, die Attribute, die in dieser Klasse
Spielfigur
festgelegt werden, zu finden: hier sind dasfarbe
(vom TypString
),punkte
(vom Typint
) undwürfel
(vom TypWürfel
– diese Variable speichert eine sogenannte Referenz auf einWürfel
-Objekt. Was das genau ist, wirst du in diesem Kurs noch lernen. Für den Augenblick kannst du dir eine Referenz als eine Art Fernbedienung vorstellen, mit der man ein Würfel-Objekt steuern kann.) - Manche der Methoden haben Rückgabewerte –
int
,boolean
oderWürfel
(beim letzten wird die Fernbedienung für ein Würfel-Objekt zurückgegeben.) Andere sind mitvoid
gekennzeichnet und geben also nichts zurück. Sieh’ dir diese Methoden genauer an: manche geben etwas am Bildschirm aus (zeigePunkte()
), andere verändern etwas am Objektzustand (erhalteWürfel
undwürfle
). (Diese Veränderungen nennt man übrigens Seiteneffekte.)
/*
* b und r spielen ein Würfelspiel.
* Beide würfeln abwechselnd und addieren ihre Würfe.
* Wer zuerst mehr als 100 Punkte hat, hat gewonnen.
*/
class Spiel {
Spielfigur b;
Spielfigur r;
Würfel w;
public static void main(String[] args) {
Spiel spiel = new Spiel();
spiel.los();
}
Spiel() {
b = new Spielfigur("blue");
r = new Spielfigur("red");
w = new Würfel(6);
b.erhalteWürfel(w);
}
void los() {
while (!(b.hatGewonnen()) && !(r.hatGewonnen())) {
b.würfle();
b.zeigePunkte();
r.erhalteWürfel(b.gibWürfel());
r.würfle();
r.zeigePunkte();
b.erhalteWürfel(r.gibWürfel());
}
}
}
- Die erste Zeile beginnt hier mit den Zeichen
/*
– damit beginnt ein mehrzeiliger Kommentar. Dieser wird beendet in der Zeile mit*/
. Kommentare liefern menschlichen Leserinnen und Lesern (hoffentlich) nützliche Hinweise und werden bei der Ausführung vom Computer ignoriert. - In der Online-IDE können wir eigentlich einfach drauflosschreiben – dann wird unser Code Zeile für Zeile ausgeführt. Java-Programme haben hingegen üblicherweise einen Einstiegspunkt, von dem aus das Programm gestartet wird. Das ist die main-Methode, die mit der Zeile
public static void main(String[] args)
losgeht. Am Ende dieses Kurses sollst du alle hier enthaltenen Wörter verstehen, jetzt genügt zu wissen, dass beimain
(engl. für so etwas wie »Hauptsache«) unser Programm startet. Hier wird einSpiel
-Objekt erzeugt (ja, die Klasse erzeugt hier ein Objekt von sich selbst!), und an diesem Objekt die Methodelos()
aufgerufen. - Der Konstruktor
Spiel
kümmert sich darum, dass alle für das Spiel benötigten Objekte erzeugt werden. Inb
undr
werden Spielfigur-Referenzen abgespeichert, inw
eine Würfel-Referenz. (Hier hätte man auchthis.b
,this.r
undthis.w
schreiben können. Weil es aber keine lokalen Variablen gibt, die genauso heißen, können wir uns dasthis
hier sparen.) Am Ende der Konstruktormethode bekommt Spielerb
das Würfelobjekt. - In der Methode
los()
steckt nun das eigentliche Spiel. Die Zeilebedeutet, wenn man sie übersetzt:while (!(b.hatGewonnen()) && !(r.hatGewonnen())) {
Die öffnende geschweifte Klammer zum Schluss leitet dann den Block ein, den dieseSolange b nicht gewonnen hat UND r nicht gewonnen hat …
while
-Wiederholung von vorne beginnt, bis eines der Spielfigur-Objekte gewonnen hat:b
würfelt, zeigt die Punkte, übergibt den Würfel anr
(r.erhalteWürfel(b.gibWürfel())
ist eine tolle Zeile oder?),r
würfelt, zeigt die Punkte, und gibt den Würfel zurück.
Die im Folgenden gezeigten Abschnitt können alle in einer Datei stehen.
from random import randint
from time import sleep
- Hier werden aus zwei Modulen der Standardbibliothek Funktionen importiert.
randint
ist eine Funktion, die aus einem gegebenen Intervall eine Zufallszahl zurückgibt.sleep
pausiert die Ausführung eines Programmes für eine gewünschte Zeit (in Sekunden).
class Würfel:
def __init__(self, seiten: int):
self.seiten = seiten
def würfle(self):
return randint(1, self.seiten)
- Offensichtlich möchte hier jemand Würfel-Objekte erzeugen.
class Würfel
eröffnet die Beschreibung dieser Objekte. (Erzeugt werden sie dann, mit dieser Klasse als Bauplan, woanders.) - Die Methode
__init__
ist die Konstruktor-Methode. Mit(self, seiten: int)
nimmt sie schon bei der Erzeugung eines neuen Würfel-Objekts die Anzahl der Seiten entgegen, und speichert diese mitself.seiten = seiten
in der hier initialisierten Instanzvariableseiten
ab. Der Konstruktor hat keinereturn
-Anweisung, gibt aber ein Objekt der KlasseWürfel
zurück. Ein anderes Wort für Instanzvariable ist Attribut. - Die Klasse
Würfel
legt eine einzige Methode (neben dem Konstruktor) für Würfel-Objekte fest, nämlich mitwürfle
das Würfeln. Die Methode ist sehr kurz, sie gibt (mit derreturn
-Anweisung) eine Zufallszahl zwischen 1 und der Anzahl der Seiten des Würfels zurück.
class Spielfigur:
def __init__(self, nummer):
self.nummer = nummer
self.punkte = 0
def zeige_punkte(self):
print('Spieler ' + str(self.nummer) + ': Ich habe ' + str(self.punkte) + ' Punkte.')
def gib_punkte(self):
return self.punkte
def erhalte_würfel(self, würfel):
self.würfel = würfel
def gib_würfel(self):
w = self.würfel
self.würfel = None
return w
def würfle(self):
self.punkte += self.würfel.würfle()
def hat_gewonnen(self):
gewonnen = self.punkte > 100
if gewonnen:
print('Spieler ' + str(self.nummer) + ': Ich habe gewonnen!')
return gewonnen
- Du solltest jetzt in der Lage sein, die Attribute, die in dieser Klasse
Spielfigur
festgelegt werden, zu finden: hier sind dasnummer
,punkte
undwürfel
(vom TypWürfel
– diese Variable speichert eine sogenannte Referenz auf einWürfel
-Objekt. Was das genau ist, wirst du in diesem Kurs noch lernen. Für den Augenblick kannst du dir eine Referenz als eine Art Fernbedienung vorstellen, mit der man ein Würfel-Objekt steuern kann.) - Manche der Methoden haben Rückgabe-Anweisungen – sie geben
int
,boolean
oderWürfel
zurück (beim letzten wird die Fernbedienung für ein Würfel-Objekt zurückgegeben.) Andere geben nichts zurück. Sieh’ dir diese Methoden genauer an: manche geben etwas am Bildschirm aus (zeige_punkte()
), andere verändern etwas am Objektzustand (erhalte_würfel
undwürfle
). (Diese Veränderungen nennt man übrigens Seiteneffekte.)
def main():
eins = Spielfigur(1)
zwei = Spielfigur(2)
w = Würfel(6);
eins.erhalte_würfel(w)
while not eins.hat_gewonnen() and not zwei.hat_gewonnen():
eins.würfle()
eins.zeige_punkte()
zwei.erhalte_würfel(eins.gib_würfel())
zwei.würfle()
zwei.zeige_punkte()
eins.erhalte_würfel(zwei.gib_würfel())
sleep(.3)
if __name__ == '__main__':
main()
- Mit Python können wir eigentlich einfach drauflosschreiben – dann wird unser Code Zeile für Zeile ausgeführt. Komplexere Programme haben hingegen üblicherweise einen Einstiegspunkt, von dem aus das Programm gestartet wird. Das ist hier die main-Funktion, die mit der Zeile
def main():
losgeht. Sie wird in der letzten Zeile aufgerufen, nachdem abgeprüft wurde, ob das Programm selbst ausgeführt wurde. (Der Vergleich schlägt fehl, wenn das Programm nur als Modul irgendwoanders eingebunden wird.) - In der
main
-Funktion werden alle für das Spiel benötigten Objekte erzeugt . Ineins
undzwei
werden Spielfigur-Referenzen abgespeichert, inw
eine Würfel-Referenz. Am Ende bekommt Spielereins
das Würfelobjekt. - In der
while
-Wiederholung steckt nun das eigentliche Spiel. Die Zeilebedeutet, wenn man sie übersetzt:while not eins.hat_gewonnen() and not zwei.hat_gewonnen():
Der Doppelpunkt zum Schluss leitet dann den Block ein, den dieseSolange eins nicht gewonnen hat UND zwei nicht gewonnen hat …
while
-Wiederholung von vorne beginnt, bis eines der Spielfigur-Objekte gewonnen hat:eins
würfelt, zeigt die Punkte, übergibt den Würfel anzwei
(zwei.erhalte_würfel(eins.gib_würfel())
ist eine tolle Zeile oder?),zwei
würfelt, zeigt die Punkte, und gibt den Würfel zurück.
Aufgabe: das Spiel erweitern #
Hier spielen nun zwei Objekte gegeneinander – ist das schon ein richtiges Spiel?
Erweitere das Spiel! Bekommst du es hin, dass das Spiel Eingaben vom menschlichen Spieler erwartet, z.B. so?
Ich habe 4 Punkte.
Du bist dran. Würfle, und gib dann deine Zahl ein:
3
Der menschliche Spieler hat 3.
Ich habe 8 Punkte.
Du bist dran. Würfle, und gib dann deine Zahl ein:
4
Der menschliche Spieler hat 7.
Ich habe 14 Punkte.
…
Tipps für Online-IDE Java #
Tipp 1
Schreibe eine Klasse MenschlicherSpieler
, die von der Klasse Spielfigur
erbt. Dann kannst du die Methoden zeigePunkte
und würfle
überschreiben, um das Verhalten anzupassen.
Mit einer solchen Klasse musst du am Spiel fast nichts ändern. Statt eines zweiten Spielfigur
-Objekts genügt es nun, mit r = new MenschlicherSpieler("red")
in r
eine Referenz auf ein MenschlicherSpieler
-Objekt zu haben. (Nicht einmal der Typ der Variable muss geändert werden!)
Tipp 2
Wie ging das gleich mit dem Erben nochmal? extends
in der ersten Zeile macht klar, von welcher Klasse geerbt wird:
class MenschlicherSpieler extends Spielfigur {
Tipp 3
Um eine Methode zu überschreiben genügt es, in der neuen Klasse eine gleich benannte Methode zu schreiben, die daneben den gleichen Rückgabetypen hat und die gleichen Parameter entgegennimmt.
void zeigePunkte() {
println("Der menschliche Spieler hat " + this.punkte + ".", farbe);
}
Tipp 4
Das Einlesen von Zahlen lässt sich in der Online-IDE mit der statischen Input
-Klasse erledigen. Diese beiden Zeilen (in einer neuen würfle
-Methode) lassen die menschliche Spielerin oder den Spieler eine Zahl eingeben und addieren diese Zahl zu den vorhandenen Punkten hinzu.
int gewürfelteZahl = Input.readInt("Du bist dran. Würfle, und gib dann deine Zahl ein: ");
this.punkte = this.punkte + gewürfelteZahl;
Lösungsvorschlag
Die Klasse MenschlicherSpieler
class MenschlicherSpieler extends Spielfigur {
void zeigePunkte() {
println("Der menschliche Spieler hat " + this.punkte + ".", farbe);
}
void würfle() {
int gewürfelteZahl = Input.readInt("Du bist dran. Würfle, und gib dann deine Zahl ein: ");
this.punkte = this.punkte + gewürfelteZahl;
}
}
Nur eine Änderung in der Klasse Spiel
class Spiel {
Spielfigur b;
Spielfigur r;
Würfel w;
public static void main(String[] args) {
Spiel spiel = new Spiel();
spiel.los();
}
Spiel() {
b = new Spielfigur("blue");
r = new MenschlicherSpieler("red");
w = new Würfel(6);
b.erhalteWürfel(w);
}
void los() {
while (!(b.hatGewonnen()) && !(r.hatGewonnen())) {
b.würfle();
b.zeigePunkte();
r.erhalteWürfel(b.gibWürfel());
r.würfle();
r.zeigePunkte();
b.erhalteWürfel(r.gibWürfel());
}
}
}
Tipps für Python #
Tipp 1
Schreibe eine Klasse MenschlicherSpieler
, die von der Klasse Spielfigur
erbt. Dann kannst du die Methoden zeige_punkte
und würfle
überschreiben, um das Verhalten anzupassen.
Mit einer solchen Klasse musst du am Spiel fast nichts ändern. Statt eines zweiten Spielfigur
-Objekts genügt es nun, mit zwei = MenschlicherSpieler(2)
in zwei
eine Referenz auf ein MenschlicherSpieler
-Objekt zu haben.
Tipp 2
Wie ging das gleich mit dem Erben nochmal? In den Klammern hinter der Klassenbezeichnung wird auf die Klasse verwiesen, von der geerbt wird.
class MenschlicherSpieler(Spielfigur):
Tipp 3
Um eine Methode zu überschreiben genügt es, in der neuen Klasse eine gleich benannte Methode zu schreiben, die die gleichen Parameter entgegennimmt.
def zeige_punkte(self):
print('Spieler ' + str(self.nummer) + '(menschlich): Ich habe ' + str(self.punkte) + ' Punkte.');
Tipp 4
Das Einlesen von Zahlen lässt sich in Python mit der eingebauten input
-Funktion erledigen. Diese beiden Zeilen (in einer neuen würfle
-Methode) lassen die menschliche Spielerin oder den Spieler eine Zahl eingeben und addieren diese Zahl zu den vorhandenen Punkten hinzu.
erwürfelt = int(input('Du bist dran. Würfle, und gib dann deine Zahl ein: '))
self.punkte += erwürfelt
Lösungsvorschlag
Die Klasse MenschlicherSpieler
class MenschlicherSpieler(Spielfigur):
def zeige_punkte(self):
print('Spieler ' + str(self.nummer) + '(menschlich): Ich habe ' + str(self.punkte) + ' Punkte.');
def würfle(self):
erwürfelt = int(input('Du bist dran. Würfle, und gib dann deine Zahl ein: '))
self.punkte += erwürfelt
Nur eine Änderung in der main
-Funktion:
def main():
eins = Spielfigur(1)
zwei = MenschlicherSpieler(2)
w = Würfel(6);
eins.erhalte_würfel(w)
while not eins.hat_gewonnen() and not zwei.hat_gewonnen():
eins.würfle()
eins.zeige_punkte()
zwei.erhalte_würfel(eins.gib_würfel())
zwei.würfle()
zwei.zeige_punkte()
eins.erhalte_würfel(zwei.gib_würfel())
sleep(.3)