Die Webseiten der Fachschaft Informatik am ERG Saalfeld


OOP am Beispiel von "Türme von Hanoi" mit Turtle


Es soll hier das Spiel "Türme von Hanoi" als Animation mit turtle, also im Grafikmodus programmiert werden. Als Vorlage dient die Umsetzung in Python im Textmodus.



Vorbereitung

Die Turtle kennt die Anweisung "setpos", die für mich dasselbe leistet wie print_at bzw. gotoxy bei Pascal. Es geht in der Vorbereitung darum heraus zu finden, wo liegt der Koordinatenursprung und welcher Bereich steht zur Verfügung. Dazu habe ich ein kleines Programm erstellt auf einer extra Seite dargestellt.

Für die linke obere Ecke ergibt sich ungefähr (-350 | 300).
Für die rechte untere Ecke ergibt sich ungefähr (350 | -300).


Die Darstellung der Scheiben

Die Darstellung der Scheiben (und des Bodens) ist aufwendiger als im Textmodus und deshalb habe ich auch diesen Schritt auf eine extra Seite ausgelagert. Dort wird Folgendes erstellt:

my_disc_farbe  = ["white", "crimson", "brown", "peru", "goldenrod", "green", "lime green", "cadet blue", "blue"]
my_disc_breite = [160, 20, 40, 60, 80, 100, 120, 140, 160]

Dabei ist die Scheibe (disc) mit der Nummer 0 die Scheibe zum Löschen.


Klassendefinitionen

Scheibe

Die Scheiben haben eine Farbe. Die ist in "my_disc_farbe" definiert.
Die Scheiben haben eine Breite. Die ist in "my_disc_breite" definiert. Damit ergibt sich für die Klasse Scheibe:

class Scheibe():
    def __init__(self, nummer_scheibe):
        Scheibe.farbe  = my_disc_farbe[nummer_scheibe]
        Scheibe.breite = my_disc_breite[nummer_scheibe]


Jede Scheibe soll sich selbst darstellen können, also zeichnen und auch wieder löschen. Um die Scheibe zu zeichnen, muss die Turtle ein Rechteck mit der entsprechenden Breite zeichnen und der vorgegebenen Farbe füllen. Die beiden Gleichungen zur Berechnung der Koordinaten werden auf einer extra Seite hergeleitet. Diese sind:

pos_x = 250 * stab - 520 - self.breite / 2
pos_y = 25 * scheiben_nr_auf_stab - 75


Die Methode "zeichnen" für die Scheibe sieht damit so aus:

    def zeichnen(self, stab, scheiben_nr_auf_stab):
        pos_x  =  250 * stab - 520  -  self.breite / 2
        pos_y  =   25 * scheiben_nr_auf_stab - 75
        my_turtle.color(self.farbe)
        my_turtle.penup()
        my_turtle.setpos(pos_x,pos_y)
        my_turtle.pendown()
        my_turtle.begin_fill()
        my_turtle.forward(self.breite)
        my_turtle.left(90)
        my_turtle.forward(23)
        my_turtle.left(90)
        my_turtle.forward(self.breite)
        my_turtle.left(90)
        my_turtle.forward(23)
        my_turtle.left(90)
        my_turtle.end_fill()


Die Methode "loeschen" funktioniert genauso wie das zeichnen, nur das eben mit einer weißen Scheibe mit der größten Breite, also my_disc[0], gezeichnet wird. Die Methode "loeschen" sieht damit so aus:

    def loeschen(self, stab, scheiben_nr_auf_stab):
        pos_x  =  250 * stab - 520  -  my_disc_breite[0] / 2
        pos_y  =   25 * scheiben_nr_auf_stab - 75
        my_turtle.color(my_disc_farbe[0])
        my_turtle.penup()
        my_turtle.setpos(pos_x,pos_y)
        my_turtle.pendown()
        my_turtle.begin_fill()
        my_turtle.forward(my_disc_breite[0])
        my_turtle.left(90)
        my_turtle.forward(23)
        my_turtle.left(90)
        my_turtle.forward(my_disc_breite[0])
        my_turtle.left(90)
        my_turtle.forward(23)
        my_turtle.left(90)
        my_turtle.end_fill()


zusammengefasst: die Klassendefinition für Scheibe sieht so aus:

class Scheibe():
    def __init__(self, nummer_scheibe):
        Scheibe.farbe  = my_disc_farbe[nummer_scheibe]
        Scheibe.breite = my_disc_breite[nummer_scheibe]

    def zeichnen(self, stab, scheiben_nr_auf_stab):
        pos_x  =  250 * stab - 520  -  self.breite / 2
        pos_y  =   25 * scheiben_nr_auf_stab - 75
        my_turtle.color(self.farbe)
        my_turtle.penup()
        my_turtle.setpos(pos_x,pos_y)
        my_turtle.pendown()
        my_turtle.begin_fill()
        my_turtle.forward(self.breite)
        my_turtle.left(90)
        my_turtle.forward(23)
        my_turtle.left(90)
        my_turtle.forward(self.breite)
        my_turtle.left(90)
        my_turtle.forward(23)
        my_turtle.left(90)
        my_turtle.end_fill()

    def loeschen(self, stab, scheiben_nr_auf_stab):
        pos_x  =  250 * stab - 520  -  my_disc_breite[0] / 2
        pos_y  =   25 * scheiben_nr_auf_stab - 75
        my_turtle.color(my_disc_farbe[0])
        my_turtle.penup()
        my_turtle.setpos(pos_x,pos_y)
        my_turtle.pendown()
        my_turtle.begin_fill()
        my_turtle.forward(my_disc_breite[0])
        my_turtle.left(90)
        my_turtle.forward(23)
        my_turtle.left(90)
        my_turtle.forward(my_disc_breite[0])
        my_turtle.left(90)
        my_turtle.forward(23)
        my_turtle.left(90)
        my_turtle.end_fill()


Die Klassendefinition für Stab zeichnet ja nicht (wohlgemerkt von Stab() - nicht für StabX() ), also kann die direkt von dem Programm im Textmodus übernommen werden,

class Stab():
    def __init__(self,nr):
        self.anzahl = 0
        self.nummer = nr      # z.B.: stab_a = 1, stab_b = 2, stab_c = 3
        self.scheiben = []      # Liste von Scheiben, die auf diesem Stab sind

    def scheibe_aufnehmen(self, nummer_scheibe):
        self.scheiben.append(nummer_scheibe)
        self.anzahl += 1                      # Anzahl um 1 erhöhen
        hilf = Scheibe(nummer_scheibe)
        stab_nr = self.nummer
        scheiben_nr_auf_stab = self.anzahl
        hilf.zeichnen(stab_nr, scheiben_nr_auf_stab)

    def scheibe_abgeben(self):
        aktuelle_scheibe = self.scheiben.pop() # Rückgabe ist Nummer der Scheibe (int)
        stab_nr = self.nummer
        scheiben_nr_auf_stab = self.anzahl
        hilf = Scheibe(scheiben_nr_auf_stab)
        hilf.loeschen(stab_nr, scheiben_nr_auf_stab)
        self.anzahl -= 1                       # Anzahl um 1 verkleinert
        return aktuelle_scheibe            # das ist die Nummer der Scheibe / my_disc


genauso wie die rekursive Prozedur "bewege".

def bewege (anzahl,stab_a,stab_b,stab_c):
    if anzahl > 0:
        bewege(anzahl-1,stab_a,stab_c,stab_b)
        aktuelle_disc = stab_a.scheibe_abgeben()
        stab_c.scheibe_aufnehmen(aktuelle_disc)
        time.sleep(5/10)                 # sonst ist das Programm viel zu schnell
        bewege(anzahl-1,stab_b,stab_a,stab_c)

Auch das Hauptprogramm kann fast vollständig übernommen werden (übernommen heißt hier kopiert werden). Allerdings geht nicht die Ausgabe "Bin fertig" - bräuchte man auch nicht, weil der Cursor ja nicht positioniert werden muss und auch die Anzahl der Bewegungen (dort mit print_at) kann so nicht ausgegeben werden. Dafür verwenden wir hier die Turtle-Funktion ...

 

Hier das vollständige Programm. Und hier als PDF-Datei. Hier das gezippte Python-Programm "turtle-hanoi.py" zum Download.

Das Programm wird z.B. so aufgerufen: python turtle-hanoi.py 5     dabei muss die Anzahl der Scheiben (von 1 bis 8) als Parameter übergeben werden.

 

zurück


© ERG Saalfeld   -   HD. Kirmse   13.05.2023