(ab Klasse 5, ohne Mathematik)

Wir programmieren das Spiel "Stein-Papier-Schere" als Zwei-Personen-Konsolen-Spiel mit Python.

Erst wird das fertige Programm vorgeführt. Dann programmiert jeder selber.

Die folgenden Code-Beispiele am besten selber abtippen.

1. Minimales Programm

Schau dir in der Python-Referenz unter "Aufbau py-Datei" an wie man ein minimales Programm schreibt.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

print("Hallo zum Stein-Papier-Schere-Spiel.")
print("Mehr gibt es hier noch nicht.")

Den Code in eine Datei speichern und laufen lassen.

Übung

  1. Lasse das Programm noch etwas anderes ausgeben.

  2. Versuche, etwas zu "zeichnen", z. B. einen Kasten.

2. Zwei Spieler

Das Spiel soll mit zwei Spielern gespielt werden. Die beiden Spielernamen geben wir auf der Konsole aus.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

print("------------------------------------")
print("Hallo zum Stein-Papier-Schere-Spiel.")
print("------------------------------------")
print("")

# spieler1 ist eine Variable.

# "Kurt" steht in Anführungszeichen und ist somit eine Zeichenkette.

# Durch das das Gleichzeichen (=) wird der Wert auf rechten Seite ("Kurt")
#  der Variablen auf der linken Seite zugewiesen.

spieler1 = "Kurt"
spieler2 = "Lea"

print("Spieler 1                           Spieler 2")

# Die format-Funktion arbeitet auf Zeichenketten.

print("{0}                                {1}".format(spieler1, spieler2))

print("")

Neue Begriffe: Zeichenkette, Variable, Zuweisung

Übung

  1. Programmiere den Spieltitel in eine Variable und ändere ihn ab.

3. Tasten

Jeder Spieler soll Tasten bekommen. Im ersten Schritt zeigen wir diese Tasten erst einmal nur auf der Konsole an und fügen Test-Code für die Tastenbehandlung ein.

Den neuen kompliziert aussehenden Code-Abschnitt im oberen Teil muss man erst mal nicht verstehen; hier am besten Kopieren&Einfügen.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

import sys

###############################################################################
### Damit wir Tasten auslesen können.                                       ###

class _Getch:
    # found on http://code.activestate.com/recipes/134892-getch-like-unbuffered-character-reading-from-stdin/

    """Gets a single character from standard input.  Does not echo to the screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): return self.impl()

class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()

getch = _Getch()

###############################################################################

print("------------------------------------")
print("Hallo zum Stein-Papier-Schere-Spiel.")
print("------------------------------------")
print("")

spieler1 = "Kurt"
spieler2 = "Lea"

print("Spieler 1                           Spieler 2")
print("{0}                                {1}".format(spieler1, spieler2))
print("")

print("Es gelten folgende Tasten:")
print("")
print("A = Stein                           J = Stein")
print("S = Schere                          K = Schere")
print("D = Papier                          L = Papier")
print("")

print("Tastentest \n   - Jeder Spieler drückt seine Tasten (gerne auch ungültige Tasten):")
print("   - Q zum Beenden")
print("")

# while True ist eine Unendlichschleife.

while True:
    k = getch()
    print("Taste: " + k)
    if k == 'q':
        break

Übung

  1. Ändere das Programm, indem du unten das k = getch() durch k = "Hallo" ersetzt. (Man kann ein Programm übrigens mit Strg+C abbrechen.)

  2. Mache die Änderung aus 1. rückgängig. Ändere das Programm so, dass es auch bei der Taste X abbricht.

  3. Was passiert, wenn man ein großes X (also Umschalt+X) drückt?

  4. Entferne die while-Anweisung (und rücke den Code um eine Stufe nach links). Was passiert?

Neue Begriffe: Schleife, if-Anweisung, Vergleichsoperator

4a. Listen

Listen am Beispiel einer Einkaufsliste.

einkaufsliste = [ "Äpfel", "Birnen", "Haferflocken", "Haselnüsse" ]

print("Ich kaufe heute folgendes ein: {0}".format(einkaufsliste))

print("Meine Einkaufsliste hat {0} Einträge.".format(len(einkaufsliste)))

einkaufsliste = einkaufsliste + [ "Aprikosen" ]
print(einkaufsliste)

einkaufsliste.append("Kakaopulver")
print(einkaufsliste)

print(einkaufsliste[0])
print(einkaufsliste[1])
print(einkaufsliste[2])
print(einkaufsliste[3])

i = 0
while i < len(einkaufsliste):
    print(einkaufsliste[i])
    i = i + 1

for eintrag in einkaufsliste:
    print(eintrag)

einkaufsliste.remove("Birnen")
print(einkaufsliste)

del einkaufsliste[0]
print(einkaufsliste)

4b. Funktionen

4c. Programm umschreiben

Wir schreiben das Programm folgendermaßen um:

  • Jedes Objekt (Stein, Papier, Schere) bekommt eine Zahl, nämlich 0, 1 und 2 → mit Hilf der Liste objekte

  • Die Tasten für jeden der Spieler wird in einer Liste von Listen definiert → Variable tasten

  • Die Spieler werden in einer Liste gespeichert → Variable spieler

  • Die Info-Ausgabe für die Tasten wird mit Hilfe der Variablen objekte und tasten umgesetzt.

  • Es gibt eine neue Funktion taste_zu_spieler, die zu einer gegebenen Taste ausgibt, zu welchem Spieler sie gehört.

  • Tastentest: zu jeder Tasten wird nun ausgegeben, zu welchem Spieler sie gehört.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

import sys

###############################################################################
### Damit wir Tasten auslesen können.                                       ###

class _Getch:
    # found on http://code.activestate.com/recipes/134892-getch-like-unbuffered-character-reading-from-stdin/

    """Gets a single character from standard input.  Does not echo to the screen."""
    def __init__(self):
        try:
            self.impl = _GetchWindows()
        except ImportError:
            self.impl = _GetchUnix()

    def __call__(self): return self.impl()

class _GetchUnix:
    def __init__(self):
        import tty, sys

    def __call__(self):
        import sys, tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()

getch = _Getch()

###############################################################################
### Globale Variablen

objekte = [ "Stein", "Papier", "Schere" ]

# erste Zeile für Spieler 1 und zweite Zeile für Spieler 2
tasten = [ [ 'A', 'S', 'D' ],
           [ 'J', 'K', 'L' ]
         ]

###############################################################################
### Funktionen                                                              ###

def taste_zu_spieler(k):
    if k.upper() in tasten[0]:
        return 0
    elif k.upper() in tasten[1]:
        return 1
    else:
        return -1

###############################################################################

print("------------------------------------")
print("Hallo zum Stein-Papier-Schere-Spiel.")
print("------------------------------------")
print("")

spieler1 = "Kurt"
spieler2 = "Lea"

spieler = [ spieler1, spieler2 ]

print("Spieler 1                           Spieler 2")
print("{0}                                {1}".format(spieler[0], spieler[1]))
print("")

print("Es gelten folgende Tasten:")
print("")
print("{0} = {1}                           {2} = {3}".format(tasten[0][0], objekte[0], tasten[1][0], objekte[0]))
print("{0} = {1}                          {2} = {3}".format(tasten[0][1], objekte[1], tasten[1][1], objekte[1]))
print("{0} = {1}                          {2} = {3}".format(tasten[0][2], objekte[2], tasten[1][2], objekte[2]))
print("")

print("Tastentest \n   - Jeder Spieler drückt seine Tasten (gerne auch ungültige Tasten):")
print("   - Q zum Beenden")
print("")

while True:
    k = getch()
    print("Taste: " + k)
    s = taste_zu_spieler(k)
    if s >= 0:
        print("Die Taste gehört zu {0}".format(spieler[s]))
    if k == 'q':
        print("Beenden...")
        break

Übung

  1. Schreibe eine eigene Funktion, die einige der print-Ausgaben kapselt.

  2. Was macht die verwendete Funktion upper(), wenn man sie auf eine Zeichenkette anwendet? Und was macht lower()?

5. Code auslagern

  • Wir lagern den getch-Code in eine eigene Datei aus. → getch.py

  • Zu jeder gedrückten Taste wird nun der Spieler und das Objekt angezeigt.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

import sys
from getch import *

###############################################################################
### Globale Variablen

objekte = [ "Stein", "Papier", "Schere" ]

# erste Zeile für Spieler 1 und zweite Zeile für Spieler 2
tasten = [ [ 'A', 'S', 'D' ],
           [ 'J', 'K', 'L' ]
         ]

###############################################################################
### Funktionen                                                              ###

def taste_zu_spieler(k):
    if k.upper() in tasten[0]:
        return 0
    elif k.upper() in tasten[1]:
        return 1
    else:
        return -1

def taste_zu_objekt(k):
    k = k.upper() # Großbuchstabe

    if k in tasten[0]:
        return tasten[0].index(k)
    elif k in tasten[1]:
        return tasten[1].index(k)
    else:
        return -1

###############################################################################

print("------------------------------------")
print("Hallo zum Stein-Papier-Schere-Spiel.")
print("------------------------------------")
print("")

spieler1 = "Kurt"
spieler2 = "Lea"

spieler = [ spieler1, spieler2 ]

print("Spieler 1                           Spieler 2")
print("{0}                                {1}".format(spieler[0], spieler[1]))
print("")

print("Es gelten folgende Tasten:")
print("")
print("{0} = {1}                           {2} = {3}".format(tasten[0][0], objekte[0], tasten[1][0], objekte[0]))
print("{0} = {1}                          {2} = {3}".format(tasten[0][1], objekte[1], tasten[1][1], objekte[1]))
print("{0} = {1}                          {2} = {3}".format(tasten[0][2], objekte[2], tasten[1][2], objekte[2]))
print("")

print("Tastentest \n   - Jeder Spieler drückt seine Tasten (gerne auch ungültige Tasten):")
print("   - Q zum Beenden")
print("")

while True:
    k = getch()
    s = taste_zu_spieler(k)
    if s >= 0:
        o = taste_zu_objekt(k)
        print("Die Taste gehört zum {0} von {1}".format(objekte[o], spieler[s]))
    else:
        print("Taste: " + k)
    if k == 'q':
        print("Beenden...")
        break

Übung

  1. Gegeben folgende Liste: a = [ 39, 0, 28, 29, 30, 5, 7, 8 ]. Was liefert der Funktionsaufruf a[2]? Und was a.index(28)?

  2. Man kann Listen auch verschachteln: a = [ 1, 2, 3 ], b = [ 10, 20, 30 ], c = [ a, b ]. Was liefert c[0][2]?

6. Spielverlauf, Logik

  • Ein einfacher Spielverlauf wird umgesetzt nach dem Muster "Achtung, Fertig, Los".

  • Zur Prüfung, ob zwei gedrückte Tasten zu unterschiedlichen Spielern gehören, werden Vergleichs- (>=) und boolsche Operatoren (and) verwendet.

  • Die Tastenlegende wurde mit einer Schleife umgesetzt.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

import sys
from getch import *

###############################################################################
### Globale Variablen

objekte = [ "Stein", "Papier", "Schere" ]

# erste Zeile für Spieler 1 und zweite Zeile für Spieler 2
tasten = [ [ 'A', 'S', 'D' ],
           [ 'J', 'K', 'L' ]
         ]

###############################################################################
### Funktionen                                                              ###

def taste_zu_spieler(k):
    if k.upper() in tasten[0]:
        return 0
    elif k.upper() in tasten[1]:
        return 1
    else:
        return -1

def taste_zu_objekt(k):
    k = k.upper() # Großbuchstabe

    if k in tasten[0]:
        return tasten[0].index(k)
    elif k in tasten[1]:
        return tasten[1].index(k)
    else:
        return -1

def ready():
    print("Ready...")
    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(k0)
    s1 = taste_zu_spieler(k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        print("        OK")
    else:
        print("!Ungültig!")

def fight():
    print("Go...")
    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(k0)
    s1 = taste_zu_spieler(k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        obj0 = taste_zu_objekt(k0)
        obj1 = taste_zu_objekt(k1)

        if s0 == 1:
            swap = obj1
            obj1 = obj0
            obj0 = swap

        print("         {0}                                {1}".format(spieler[0], spieler[1]))
        print("      # {0} #                           # {1} #".format(objekte[obj0], objekte[obj1]))
    else:
        print("!Ungültig!")



###############################################################################

print("------------------------------------")
print("Hallo zum Stein-Papier-Schere-Spiel.")
print("------------------------------------")
print("")

spieler1 = "Kurt"
spieler2 = "Lea"

spieler = [ spieler1, spieler2 ]

print("Spieler 1                           Spieler 2")
print("{0}                                {1}".format(spieler[0], spieler[1]))
print("")

print("Es gelten folgende Tasten:")
print("")
for i in range(3):
    print("{0} = {1}                           {2} = {3}".format(tasten[0][i], objekte[i], tasten[1][i], objekte[i]))
print("")

print("Spielverlauf:")
print("   - Jeder Spieler drückt eine seiner Tasten zur Vorbereitung.")
print("   - Jeder Spieler nochmal drückt eine seiner Tasten zur Vorbereitung.")
print("   - Jeder Spieler drückt die Taste, mit der er seinen Gegenspieler herausfordert.")
print("")

ready()
ready()
fight()

Übung

  1. Was fällt bei der Ausgabe der neuen Tastenlegende auf?

  2. An welcher Stelle im Code werden die Werte von zwei Variablen getauscht und warum?

  3. Gebe anstatt "Ready…", "Ready…", "Go…", "Schnick…", "Schnack…", "Schnuck!!!" aus, indem du die ready()-Funktion parametrisierst.

7. Gewinnauswertung

Am Ende wird angezeigt, wer gewonnen hat und warum.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

import sys
from getch import *

###############################################################################
### Globale Variablen

objekte = [ "Stein", "Papier", "Schere" ]

# erste Zeile für Spieler 1 und zweite Zeile für Spieler 2
tasten = [ [ 'A', 'S', 'D' ],
           [ 'J', 'K', 'L' ]
         ]

###############################################################################
### Funktionen                                                              ###

def taste_zu_spieler(k):
    if k.upper() in tasten[0]:
        return 0
    elif k.upper() in tasten[1]:
        return 1
    else:
        return -1

def taste_zu_objekt(k):
    k = k.upper() # Großbuchstabe

    if k in tasten[0]:
        return tasten[0].index(k)
    elif k in tasten[1]:
        return tasten[1].index(k)
    else:
        return -1

def gewinn_aktion(obj0, obj1):
    """
    Liefert den Text wie obj0 über obj1 gewinnt.
    Wenn obj0 gegenüber obj1 verliert, wird "ungültig" geliefert
    """
    if obj0 == 0 and obj1 == 2:
        return "Stein macht Schere stumpf."
    if obj0 == 1 and obj1 == 0:
        return "Papier umwickelt Stein."
    if obj0 == 2 and obj1 == 1:
        return "Schere zerschneidet Papier."

    return "ungültig"

def auswertung(obj0, obj1):
    """
    obj0 gehört zu spieler0.
    obj1 gehört zu spieler1.

    Liefert zurück, wer gewonnen hat (0 oder 1) oder -1 für unentschieden
    """
    if obj0 == 0 and obj1 == 2 or obj0 == 1 and obj1 == 0 or obj0 == 2 and obj1 == 1:
        return 0
    elif obj1 == 0 and obj0 == 2 or obj1 == 1 and obj0 == 0 or obj1 == 2 and obj0 == 1:
        return 1
    else:
        return -1

def ready():
    print("Ready...")
    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(k0)
    s1 = taste_zu_spieler(k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        print("        OK")
    else:
        print("!Ungültig!")

def fight():
    print("Go...")
    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(k0)
    s1 = taste_zu_spieler(k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        obj0 = taste_zu_objekt(k0)
        obj1 = taste_zu_objekt(k1)

        if s0 == 1:
            swap = obj1
            obj1 = obj0
            obj0 = swap

        print("         {0}                                {1}".format(spieler[0], spieler[1]))
        print("      # {0} #                           # {1} #".format(objekte[obj0], objekte[obj1]))

        s = auswertung(obj0, obj1)
        if s >= 0:
            ga = None
            if s == 0:
                ga = gewinn_aktion(obj0, obj1)
            else:
                ga = gewinn_aktion(obj1, obj0)
            print("              __{0}__ hat gewonnen, denn {1}".format(spieler[s], ga))
        else:
            print("              UNENTSCHIEDEN")

    else:
        print("!Ungültig!")



###############################################################################

print("------------------------------------")
print("Hallo zum Stein-Papier-Schere-Spiel.")
print("------------------------------------")
print("")

spieler1 = "Kurt"
spieler2 = "Lea"

spieler = [ spieler1, spieler2 ]

print("Spieler 1                           Spieler 2")
print("{0}                                {1}".format(spieler[0], spieler[1]))
print("")

print("Es gelten folgende Tasten:")
print("")
for i in range(3):
    print("{0} = {1}                           {2} = {3}".format(tasten[0][i], objekte[i], tasten[1][i], objekte[i]))
print("")

print("Spielverlauf:")
print("   - Jeder Spieler drückt eine seiner Tasten zur Vorbereitung.")
print("   - Jeder Spieler nochmal drückt eine seiner Tasten zur Vorbereitung.")
print("   - Jeder Spieler drückt die Taste, mit der er seinen Gegenspieler herausfordert.")
print("")

ready()
ready()
fight()

Übung

  1. Der Code ist etwas komplizierter geworden. Füge print-Anweisungen hinzu, wenn etwas unklar ist.

8. Spieler-Namen und Punkte

  • Die Namen der Spieler sind nun variabel. Sie können nun entweder als Kommandozeilenargumente übergeben werden (siehe Python-Referenz unter "Kommandozeilen-Argumente") oder werden nach Programmstart eingegeben.

  • Die Ausgabe wird besser formatiert, damit auch unterschiedliche lange Namen gut ausgegeben werden.

  • Es werden nun 3 Runden hintereinander gespielt und der Punktestand wird gezählt.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

import sys
from getch import *

###############################################################################
### Globale Variablen

objekte = [ "Stein", "Papier", "Schere" ]

# erste Zeile für Spieler 1 und zweite Zeile für Spieler 2
tasten = [ [ 'A', 'S', 'D' ],
           [ 'J', 'K', 'L' ]
         ]

punkte = [ 0, 0 ]

###############################################################################
### Funktionen                                                              ###

def taste_zu_spieler(k):
    if k.upper() in tasten[0]:
        return 0
    elif k.upper() in tasten[1]:
        return 1
    else:
        return -1

def taste_zu_objekt(k):
    k = k.upper() # Großbuchstabe

    if k in tasten[0]:
        return tasten[0].index(k)
    elif k in tasten[1]:
        return tasten[1].index(k)
    else:
        return -1

def gewinn_aktion(obj0, obj1):
    """
    Liefert den Text wie obj0 über obj1 gewinnt.
    Wenn obj0 gegenüber obj1 verliert, wird "ungültig" geliefert
    """
    if obj0 == 0 and obj1 == 2:
        return "Stein macht Schere stumpf."
    if obj0 == 1 and obj1 == 0:
        return "Papier umwickelt Stein."
    if obj0 == 2 and obj1 == 1:
        return "Schere zerschneidet Papier."

    return "ungültig"

def auswertung(obj0, obj1):
    """
    obj0 gehört zu spieler0.
    obj1 gehört zu spieler1.

    Liefert zurück, wer gewonnen hat (0 oder 1) oder -1 für unentschieden
    """
    if obj0 == 0 and obj1 == 2 or obj0 == 1 and obj1 == 0 or obj0 == 2 and obj1 == 1:
        return 0
    elif obj1 == 0 and obj0 == 2 or obj1 == 1 and obj0 == 0 or obj1 == 2 and obj0 == 1:
        return 1
    else:
        return -1

def ready():
    print("Ready... ", end="")
    sys.stdout.flush() # damit die Ausgabe sofort erscheint

    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(k0)
    s1 = taste_zu_spieler(k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        print("OK\n")
    else:
        print("!Ungültig!")

def fight():
    print("Go...")
    print("")

    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(k0)
    s1 = taste_zu_spieler(k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        obj0 = taste_zu_objekt(k0)
        obj1 = taste_zu_objekt(k1)

        if s0 == 1:
            swap = obj1
            obj1 = obj0
            obj0 = swap

        print("{0:40}{1:40}".format(spieler[0], spieler[1]))
        print("{0:40}{1:40}".format("v", "v"))
        print("{0:40}{1:40}".format(objekte[obj0], objekte[obj1]))
        print("-" * 80)

        s = auswertung(obj0, obj1)
        if s >= 0:

            # Punktestand anpassen
            punkte[s] = punkte[s] + 1

            ga = None
            if s == 0:
                ga = gewinn_aktion(obj0, obj1)
            else:
                ga = gewinn_aktion(obj1, obj0)
            print(">> {0} hat gewonnen, denn {1}".format(spieler[s], ga))
        else:
            print(">> UNENTSCHIEDEN")

        print("                    Punktestand:")
        print("{0:40}{1:40}".format(spieler[0] + " ({0})".format(punkte[0]), spieler[1] + " ({0})".format(punkte[1])))

    else:
        print("!Ungültig!")

def hole_spielernamen():
    spieler0 = None
    spieler1 = None

    if len(sys.argv) == 3:
        spieler0 = sys.argv[1]
        spieler1 = sys.argv[2]
    else:
        print("Bitte Namen per Hand eintragen.")
        spieler0 = input("Spieler 1: ")
        spieler1 = input("Spieler 2: ")
        print("")

    return [ spieler0, spieler1 ]


###############################################################################

print("     ------------------------------------")
print("     Hallo zum Stein-Papier-Schere-Spiel.")
print("     ------------------------------------")
print("")

spieler = hole_spielernamen()

print("{0:40}{1:40}".format("Spieler 1", "Spieler 2"))
print("{0:40}{1:40}".format("---------", "---------"))
print("{0:40}{1:40}".format(spieler[0], spieler[1]))
print("")

print("                       Tasten:\n")

for i in range(3):
    print("{0} = {1:36}{2} = {3:36}".format(tasten[0][i], objekte[i], tasten[1][i], objekte[i]))
print("")

print("Spielverlauf:")
print("   1. Jeder Spieler drückt eine seiner Tasten zur Vorbereitung.")
print("   2. Nochmal Vorbereitung")
print("   3. Jeder Spieler drückt die Taste, mit der er seinen Gegenspieler herausfordert.")
print("")

for i in range(3):
    ready()
    ready()
    fight()
    print("")

Übung

  1. Wenn man der interaktiven Namenseingabe nichts eingibt, bleibt der Name leer. Sorge dafür, dass der Benutzer solange wiederholt gefragt wird, bis der Name nicht leer ist.

  2. Wenn jemand gewinnt, bekommt er einen Punkt. Verteile die Punkte anders: Wer gewinnt, bekommt 5 Punkte, wer verliert, bekommt 2 Punkte abgezogen, bei unentschieden gibt es je 1 Punkt.

  3. Frage den Benutzer vorher, wieviele Runden gespielt werden sollen.

9. Piktogramme

  • Finde ein Bild mit Piktogrammen von Schere, Stein und Papier, z. B. picto-1

  • Bearbeite es z. B. mit kolourpaint so,

    • dass es einen weißen Hintergrund hat

    • die Rahmen weg sind

    • und das Format jpg ist, z. B. picto-2

  • Installiere das Programm jp2a mittels software.opensuse.org.

  • Verwende den Befehl jp2a -i --height=25 bild.jpg, um die Piktogramme in ASCII-Art (engl. art = Kunst) umzuwandeln.

  • Speichere jedes der Symbole in eine eigene Datei: schere.txt, stein.txt, papier.txt.

    • Hierbei kann der "Block Selection Mode" von Kate helfen.

  • Das Programm wird nun so geändert, dass die Bilder aus den Dateien geladen und angezeigt werden, wenn ein Spieler gewonnen hat.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

import sys
import os
from getch import *

###############################################################################
### Funktionen
###############################################################################

def taste_zu_spieler(tasten, k):
    if k.upper() in tasten[0]:
        return 0
    elif k.upper() in tasten[1]:
        return 1
    else:
        return -1

def taste_zu_objekt(tasten, k):
    k = k.upper() # Großbuchstabe

    if k in tasten[0]:
        return tasten[0].index(k)
    elif k in tasten[1]:
        return tasten[1].index(k)
    else:
        return -1

def gewinn_aktion(obj0, obj1):
    """
    Liefert den Text wie obj0 über obj1 gewinnt.
    Wenn obj0 gegenüber obj1 verliert, wird "ungültig" geliefert
    """
    if obj0 == 0 and obj1 == 2:
        return "Stein macht Schere stumpf."
    if obj0 == 1 and obj1 == 0:
        return "Papier umwickelt Stein."
    if obj0 == 2 and obj1 == 1:
        return "Schere zerschneidet Papier."

    return "ungültig"

def auswertung(obj0, obj1):
    """
    obj0 gehört zu spieler0.
    obj1 gehört zu spieler1.

    Liefert zurück, wer gewonnen hat (0 oder 1) oder -1 für unentschieden
    """
    if obj0 == 0 and obj1 == 2 or obj0 == 1 and obj1 == 0 or obj0 == 2 and obj1 == 1:
        return 0
    elif obj1 == 0 and obj0 == 2 or obj1 == 1 and obj0 == 0 or obj1 == 2 and obj0 == 1:
        return 1
    else:
        return -1

def ready(tasten):
    print("Ready... ", end="")
    sys.stdout.flush() # damit die Ausgabe sofort erscheint

    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(tasten, k0)
    s1 = taste_zu_spieler(tasten, k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        print("OK\n")
    else:
        print("!Ungültig!")

def fight(tasten, spieler, objekte, punkte, bilder):
    print("Go...")
    print("")

    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(tasten, k0)
    s1 = taste_zu_spieler(tasten, k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        obj0 = taste_zu_objekt(tasten, k0)
        obj1 = taste_zu_objekt(tasten, k1)

        if s0 == 1:
            swap = obj1
            obj1 = obj0
            obj0 = swap

        print("{0:40}{1:40}".format(spieler[0], spieler[1]))
        print("{0:40}{1:40}".format("v", "v"))
        print("{0:40}{1:40}".format(objekte[obj0], objekte[obj1]))
        print("-" * 80)

        s = auswertung(obj0, obj1)
        if s >= 0:

            if s == 0:
                print_bild(bilder, obj0, 0)
            else:
                print_bild(bilder, obj1, 40)

            # Punktestand anpassen
            punkte[s] = punkte[s] + 1

            ga = None
            if s == 0:
                ga = gewinn_aktion(obj0, obj1)
            else:
                ga = gewinn_aktion(obj1, obj0)
            print(">> {0} hat gewonnen, denn {1}".format(spieler[s], ga))
        else:
            print(">> UNENTSCHIEDEN")

        print("                    Punktestand:")
        print("{0:40}{1:40}".format(spieler[0] + " ({0})".format(punkte[0]), spieler[1] + " ({0})".format(punkte[1])))

    else:
        print("!Ungültig!")

def hole_spielernamen():
    spieler0 = None
    spieler1 = None

    if len(sys.argv) == 3:
        spieler0 = sys.argv[1]
        spieler1 = sys.argv[2]
    else:
        print("Bitte Namen per Hand eintragen.")
        spieler0 = input("Spieler 1: ")
        spieler1 = input("Spieler 2: ")
        print("")

    return [ spieler0, spieler1 ]

def lade_bilder():
    bilder = []
    with open('sp-data/stein.txt') as f:
        bilder.append(f.read())

    with open('sp-data/papier.txt') as f:
        bilder.append(f.read())

    with open('sp-data/schere.txt') as f:
        bilder.append(f.read())

    return bilder

def print_bild(bilder, obj, offset = 0):
    if offset == 0:
        print(bilder[obj])
    else:
        bild = bilder[obj]
        zeilen = bild.split(os.linesep)
        for z in zeilen:
            print(" " * offset + z)

###############################################################################
### main
###############################################################################

def main():
    print("     ------------------------------------")
    print("     Hallo zum Stein-Papier-Schere-Spiel.")
    print("     ------------------------------------")
    print("")

    objekte = [ "Stein", "Papier", "Schere" ]

    # erste Zeile für Spieler 1 und zweite Zeile für Spieler 2
    tasten = [ [ 'A', 'S', 'D' ],
               [ 'J', 'K', 'L' ]
            ]

    punkte = [ 0, 0 ]

    bilder = lade_bilder()

    spieler = hole_spielernamen()

    print("{0:40}{1:40}".format("Spieler 1", "Spieler 2"))
    print("{0:40}{1:40}".format("---------", "---------"))
    print("{0:40}{1:40}".format(spieler[0], spieler[1]))
    print("")

    print("                       Tasten:\n")

    for i in range(3):
        print("{0} = {1:36}{2} = {3:36}".format(tasten[0][i], objekte[i], tasten[1][i], objekte[i]))
    print("")

    print("Spielverlauf:")
    print("   1. Jeder Spieler drückt eine seiner Tasten zur Vorbereitung.")
    print("   2. Nochmal Vorbereitung")
    print("   3. Jeder Spieler drückt die Taste, mit der er seinen Gegenspieler herausfordert.")
    print("")

    ready(tasten)
    ready(tasten)
    fight(tasten, spieler, objekte, punkte, bilder)
    print("")

if __name__ == "__main__":
    main()

Übung

  1. Zeige zwei der Bilder nebeneinander an.

10. Punktestand merken, Klassen

  • Der Punktestand jedes Spielers soll in einer Datei gespeichert werden, sodass bei Neustart des Programms der Punktestand wiederhergestellt werden kann.

  • Das Laden und Speichern wird mit Hilfe einer Klasse gekapselt.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

import sys
import os
import pickle
from getch import *

###############################################################################
### Funktionen
###############################################################################

def taste_zu_spieler(tasten, k):
    if k.upper() in tasten[0]:
        return 0
    elif k.upper() in tasten[1]:
        return 1
    else:
        return -1

def taste_zu_objekt(tasten, k):
    k = k.upper() # Großbuchstabe

    if k in tasten[0]:
        return tasten[0].index(k)
    elif k in tasten[1]:
        return tasten[1].index(k)
    else:
        return -1

def gewinn_aktion(obj0, obj1):
    """
    Liefert den Text wie obj0 über obj1 gewinnt.
    Wenn obj0 gegenüber obj1 verliert, wird "ungültig" geliefert
    """
    if obj0 == 0 and obj1 == 2:
        return "Stein macht Schere stumpf."
    if obj0 == 1 and obj1 == 0:
        return "Papier umwickelt Stein."
    if obj0 == 2 and obj1 == 1:
        return "Schere zerschneidet Papier."

    return "ungültig"

def auswertung(obj0, obj1):
    """
    obj0 gehört zu spieler0.
    obj1 gehört zu spieler1.

    Liefert zurück, wer gewonnen hat (0 oder 1) oder -1 für unentschieden
    """
    if obj0 == 0 and obj1 == 2 or obj0 == 1 and obj1 == 0 or obj0 == 2 and obj1 == 1:
        return 0
    elif obj1 == 0 and obj0 == 2 or obj1 == 1 and obj0 == 0 or obj1 == 2 and obj0 == 1:
        return 1
    else:
        return -1

def ready(tasten):
    print("Ready... ", end="")
    sys.stdout.flush() # damit die Ausgabe sofort erscheint

    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(tasten, k0)
    s1 = taste_zu_spieler(tasten, k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        print("OK\n")
    else:
        print("!Ungültig!")

def fight(tasten, spieler, objekte, punkte, bilder):
    print("Go...")
    print("")

    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(tasten, k0)
    s1 = taste_zu_spieler(tasten, k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        obj0 = taste_zu_objekt(tasten, k0)
        obj1 = taste_zu_objekt(tasten, k1)

        if s0 == 1:
            swap = obj1
            obj1 = obj0
            obj0 = swap

        print("{0:40}{1:40}".format(spieler[0], spieler[1]))
        print("{0:40}{1:40}".format("v", "v"))
        print("{0:40}{1:40}".format(objekte[obj0], objekte[obj1]))
        print("-" * 80)

        s = auswertung(obj0, obj1)
        if s >= 0:

            if s == 0:
                print_bild(bilder, obj0, 0)
            else:
                print_bild(bilder, obj1, 40)

            # Punktestand anpassen
            punkte[s] = punkte[s] + 1

            ga = None
            if s == 0:
                ga = gewinn_aktion(obj0, obj1)
            else:
                ga = gewinn_aktion(obj1, obj0)
            print(">> {0} hat gewonnen, denn {1}".format(spieler[s], ga))
        else:
            print(">> UNENTSCHIEDEN")

        print("                    Punktestand:")
        print("{0:40}{1:40}".format(spieler[0] + " ({0})".format(punkte[0]), spieler[1] + " ({0})".format(punkte[1])))

    else:
        print("!Ungültig!")

def hole_spielernamen():
    spieler0 = None
    spieler1 = None

    if len(sys.argv) == 3:
        spieler0 = sys.argv[1]
        spieler1 = sys.argv[2]
    else:
        print("Bitte Namen per Hand eintragen.")
        spieler0 = input("Spieler 1: ")
        spieler1 = input("Spieler 2: ")
        print("")

    return [ spieler0, spieler1 ]

def lade_bilder():
    bilder = []
    with open('sp-data/stein.txt') as f:
        bilder.append(f.read())

    with open('sp-data/papier.txt') as f:
        bilder.append(f.read())

    with open('sp-data/schere.txt') as f:
        bilder.append(f.read())

    return bilder

def print_bild(bilder, obj, offset = 0):
    if offset == 0:
        print(bilder[obj])
    else:
        bild = bilder[obj]
        zeilen = bild.split(os.linesep)
        for z in zeilen:
            print(" " * offset + z)

def spieler_mit_punkte(spielername, punkte):
    return "{0} ({1})".format(spielername, punkte)

class PunkteDatei:
    def __init__(self):
        self._dateiname = '_steinpapierschere.save'
        # Ein Dictionary, das alle Benutzer enthält, die bereits gespielt haben:
        # z. B. { 'Kurt': 10, 'Lea': 20 }
        self.punktestand_gesamt = { }

    def load(self):
        try:
            with open(self._dateiname, 'rb') as f:
                self.punktestand_gesamt = pickle.load(f)
        except FileNotFoundError:
            pass

    def save(self):
        with open(self._dateiname, 'wb') as f:
            pickle.dump(self.punktestand_gesamt, f, 0)

###############################################################################
### main
###############################################################################

def main():
    print("     ---------------------------------------")
    print("       Hallo zum Stein-Papier-Schere-Spiel  ")
    print("     ---------------------------------------")
    print("")

    objekte = [ "Stein", "Papier", "Schere" ]

    # erste Zeile für Spieler 1 und zweite Zeile für Spieler 2
    tasten = [ [ 'A', 'S', 'D' ],
               [ 'J', 'K', 'L' ]
             ]

    bilder = lade_bilder()

    spieler = hole_spielernamen()

    punkte = [ 0, 0 ]

    # Punkte-Datei laden
    p_datei = PunkteDatei()
    p_datei.load()

    # und Werte zuweisen, falls bereits vorhanden
    for i in range(2):
        if spieler[i] in p_datei.punktestand_gesamt:
            punkte[i] = p_datei.punktestand_gesamt[spieler[i]]

    print("{0:40}{1:40}".format("Spieler 1", "Spieler 2"))
    print("{0:40}{1:40}".format("---------", "---------"))
    print("{0:40}{1:40}".format(spieler_mit_punkte(spieler[0], punkte[0]), spieler_mit_punkte(spieler[1], punkte[1])))
    print("")

    print("                       Tasten:\n")

    for i in range(3):
        print("{0} = {1:36}{2} = {3:36}".format(tasten[0][i], objekte[i], tasten[1][i], objekte[i]))
    print("")

    print("Spielverlauf:")
    print("   1. Jeder Spieler drückt eine seiner Tasten zur Vorbereitung.")
    print("   2. Nochmal Vorbereitung.")
    print("   3. Jeder Spieler drückt die Taste, mit der er seinen Gegenspieler herausfordert.")
    print("")

    ready(tasten)
    ready(tasten)
    fight(tasten, spieler, objekte, punkte, bilder)
    print("")

    # Punktestand speichern
    for i in range(2):
        if spieler[i] not in p_datei.punktestand_gesamt:
            p_datei.punktestand_gesamt[spieler[i]] = 0
        p_datei.punktestand_gesamt[spieler[i]] = punkte[i]
    p_datei.save()

if __name__ == "__main__":
    main()

Übung

  1. Schau dir den Inhalt der Punktestand-Datei an und versuche, die Punktzahl eines Spielers manuell zu ändern.

11. Cheat

  • Einbau einer Spezialtaste, so dass eingeweihte Personen nicht verlieren können.

    • Das heißt unter anderem, eine Funktion zu schreiben, die zu einem Objekt das passende Gewinnobjekt auswählt.

#!/usr/bin/python3

"""
Das Stein-Papier-Schere-Spiel
"""

import sys
import os
import pickle
from getch import *

###############################################################################
### Funktionen
###############################################################################

def taste_zu_spieler(tasten, k):
    if k.upper() in tasten[0]:
        return 0
    elif k.upper() in tasten[1]:
        return 1
    else:
        return -1

def taste_zu_objekt(tasten, k):
    k = k.upper() # Großbuchstabe

    if k in tasten[0]:
        return tasten[0].index(k)
    elif k in tasten[1]:
        return tasten[1].index(k)
    else:
        return -1

def gewinn_aktion(obj0, obj1):
    """
    Liefert den Text wie obj0 über obj1 gewinnt.
    Wenn obj0 gegenüber obj1 verliert, wird "ungültig" geliefert
    """
    if obj0 == 0 and obj1 == 2:
        return "Stein macht Schere stumpf."
    if obj0 == 1 and obj1 == 0:
        return "Papier umwickelt Stein."
    if obj0 == 2 and obj1 == 1:
        return "Schere zerschneidet Papier."

    return "ungültig"

def auswertung(obj0, obj1):
    """
    obj0 gehört zu spieler0.
    obj1 gehört zu spieler1.

    Liefert zurück, wer gewonnen hat (0 oder 1) oder -1 für unentschieden
    """
    if obj0 == 0 and obj1 == 2 or obj0 == 1 and obj1 == 0 or obj0 == 2 and obj1 == 1:
        return 0
    elif obj1 == 0 and obj0 == 2 or obj1 == 1 and obj0 == 0 or obj1 == 2 and obj0 == 1:
        return 1
    else:
        return -1

def ready(tasten):
    print("Ready... ", end="")
    sys.stdout.flush() # damit die Ausgabe sofort erscheint

    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(tasten, k0)
    s1 = taste_zu_spieler(tasten, k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        print("OK\n")
    else:
        print("!Ungültig!")

def fight(tasten, spieler, objekte, punkte, bilder):
    print("Go...")
    print("")

    k0 = getch()
    k1 = getch()

    s0 = taste_zu_spieler(tasten, k0)
    s1 = taste_zu_spieler(tasten, k1)

    if s0 >= 0 and s1 >= 0 and s0 != s1:
        obj = [ None, None ]
        obj[0] = taste_zu_objekt(tasten, k0)
        obj[1] = taste_zu_objekt(tasten, k1)

        if s0 == 1:
            swap = obj[1]
            obj[1] = obj[0]
            obj[0] = swap
        # obj0 gehört nun zu spieler0

        # Cheat:
        other = [ obj[1], obj[0] ]
        for i in range(2):
            if obj[i] == 3: # Cheat-Taste
                if other[i] == 0:
                    obj[i] = 1
                elif other[i] == 1:
                    obj[i] = 2
                elif other[i] == 2:
                    obj[i] = 0
                else:
                    obj[i] = 0 # beide haben geschummelt => unentschieden

        print("{0:40}{1:40}".format(spieler[0], spieler[1]))
        print("{0:40}{1:40}".format(" |", " |"))
        print("{0:40}{1:40}".format(" v", " v"))
        print("{0:40}{1:40}".format(objekte[obj[0]], objekte[obj[1]]))
        print("-" * 80)

        s = auswertung(obj[0], obj[1])
        if s >= 0:

            if s == 0:
                print_bild(bilder, obj[0], 0)
            else:
                print_bild(bilder, obj[1], 40)

            # Punktestand anpassen
            punkte[s] = punkte[s] + 1

            ga = None
            if s == 0:
                ga = gewinn_aktion(obj[0], obj[1])
            else:
                ga = gewinn_aktion(obj[1], obj[0])
            print(">> {0} hat gewonnen, denn {1}".format(spieler[s], ga))
        else:
            print(">> UNENTSCHIEDEN")

        print("                    Punktestand:")
        print("{0:40}{1:40}".format(spieler[0] + " ({0})".format(punkte[0]), spieler[1] + " ({0})".format(punkte[1])))

    else:
        print("!Ungültig!")

def hole_spielernamen():
    spieler0 = None
    spieler1 = None

    if len(sys.argv) == 3:
        spieler0 = sys.argv[1]
        spieler1 = sys.argv[2]
    else:
        print("Bitte Namen per Hand eintragen.")
        spieler0 = input("Spieler 1: ")
        spieler1 = input("Spieler 2: ")
        print("")

    return [ spieler0, spieler1 ]

def lade_bilder():
    bilder = []
    with open('sp-data/stein.txt') as f:
        bilder.append(f.read())

    with open('sp-data/papier.txt') as f:
        bilder.append(f.read())

    with open('sp-data/schere.txt') as f:
        bilder.append(f.read())

    return bilder

def print_bild(bilder, obj, offset = 0):
    if offset == 0:
        print(bilder[obj])
    else:
        bild = bilder[obj]
        zeilen = bild.split(os.linesep)
        for z in zeilen:
            print(" " * offset + z)

def spieler_mit_punkte(spielername, punkte):
    return "{0} ({1})".format(spielername, punkte)

class PunkteDatei:
    def __init__(self):
        self._dateiname = '_steinpapierschere.save'
        # Ein Dictionary, das alle Benutzer enthält, die bereits gespielt haben:
        # z. B. { 'Kurt': 10, 'Lea': 20 }
        self.punktestand_gesamt = { }

    def load(self):
        try:
            with open(self._dateiname, 'rb') as f:
                self.punktestand_gesamt = pickle.load(f)
        except FileNotFoundError:
            pass

    def save(self):
        with open(self._dateiname, 'wb') as f:
            pickle.dump(self.punktestand_gesamt, f, 0)

###############################################################################
### main
###############################################################################

def main():
    print("     ---------------------------------------")
    print("       Hallo zum Stein-Papier-Schere-Spiel  ")
    print("     ---------------------------------------")
    print("")

    objekte = [ "Stein", "Papier", "Schere" ]

    # erste Zeile für Spieler 1 und zweite Zeile für Spieler 2
    tasten = [ [ 'A', 'S', 'D', 'W' ],
               [ 'J', 'K', 'L', 'I' ]
             ]

    bilder = lade_bilder()

    spieler = hole_spielernamen()

    punkte = [ 0, 0 ]

    # Punkte-Datei laden
    p_datei = PunkteDatei()
    p_datei.load()

    # und Werte zuweisen, falls bereits vorhanden
    for i in range(2):
        if spieler[i] in p_datei.punktestand_gesamt:
            punkte[i] = p_datei.punktestand_gesamt[spieler[i]]

    print("{0:40}{1:40}".format("Spieler 1", "Spieler 2"))
    print("{0:40}{1:40}".format("---------", "---------"))
    print("{0:40}{1:40}".format(spieler_mit_punkte(spieler[0], punkte[0]), spieler_mit_punkte(spieler[1], punkte[1])))
    print("")

    print("                       Tasten:\n")

    for i in range(3):
        print("{0} = {1:36}{2} = {3:36}".format(tasten[0][i], objekte[i], tasten[1][i], objekte[i]))
    print("")

    print("Spielverlauf:")
    print("   1. Jeder Spieler drückt eine seiner Tasten zur Vorbereitung.")
    print("   2. Nochmal Vorbereitung.")
    print("   3. Jeder Spieler drückt die Taste, mit der er seinen Gegenspieler herausfordert.")
    print("")

    ready(tasten)
    ready(tasten)
    fight(tasten, spieler, objekte, punkte, bilder)
    print("")

    # Punktestand speichern
    for i in range(2):
        if spieler[i] not in p_datei.punktestand_gesamt:
            p_datei.punktestand_gesamt[spieler[i]] = 0
        p_datei.punktestand_gesamt[spieler[i]] = punkte[i]
    p_datei.save()

if __name__ == "__main__":
    main()

Lerneffekt: Traue keinem Programm, dessen Quellcode nicht eingesehen werden kann.

12. Erweiterung

Erweitere das Spiel, so dass man (Bonus: als Option) "Papier, Stein, Schere, Echse, Spock" spielen kann.