K103: Schildkröte im Sandkasten

Table of Contents

Simulation "Schildkröte im Sandkasten"

Bei unserer Simulation geht es um eine Schildkröte, welche aus dem Sandkasten möchte. Sie bewegt sich dabei rein zufällig und hat 1000 Bewegungen zur Verfügung. Wie oft schaft sie es, aus dem Sandkasten zu kommen?

Jedesmal, wenn unsere Schildkröte den Sandkastenrand berührt, wird sie wieder in zurück in die Mitte verschoben! Das nachfolgende Video zeigt unsere kleine Simulation.

Grundversion

Allgemeine Vorbereitungen

Als erstes müssen wir unsere turtle Bibliothek importieren und den Bildschirm vorbereiten. Wir wollen das nachfolgend dargestellte Fenster erzeugen. Es hat den Fenstertitel «Punktesammler», die Grösse 600×700, einen hellgrauen Hintergrund (lightgrey) und noch einen Willkommenstext. Selbstverständlich kannst du auch einen eigenen Titel und Willkommenstext wählen.

Um das Fenster zu erzeugen benötigen wir folgenden Code:

# Bibliotheken importieren
from turtle import *
import random

# Bildschirm anlegen
setup(width= 600, height=700)
title("Punktesammler")
bgcolor("lightgrey")

# Titel
penup()
goto(0,250)
write("Willkommen zu unserer Simulation", align = "center", font = ("arial", 20, "normal"))
home()

Am Anfang importieren wir neben allen Befehlen aus der turtle Bibliothek (Zeile 2: from turtle import *) gleich noch die Bibliothek random (Zeile 3: import random). Wir werden diese später benötigen, um die Schildkröte zufällig bewegen zu können.

Anschliessend erstellen wir unser Fenster (Screen). Die Anweisung setup() stellt die Grösse unseres Bildschirmfensters ein. Mit title() können wir den Fenstertitel und mit bgcolor() die Hintergrundfarbe festlegen.

Als drittes können wir noch einen Willkommenstext auf den Bildschirm schreiben. Wir fahren mit unserer Schildkröte an einen geeigneten Platz (im Beispiel nach x: 0, y: 250), ohne dabei einen Strich zu ziehen. Damit der Text schön mittig ist, gilt es align=»center» zu setzen. Ist der Text geschrieben, so kehren wir mit home() wieder zurück zur Mitte. In unserem Beispiel können wir theoretisch auch auf diese letzte Anweisung verzichten, da wir als nächstes noch den Sandkasten malen müssen.

Sandkasten malen

Als nächstes wollen wir den Sandkasten (unser Spielfeld) zeichnen. Es ist ein Quadrat in beiger (Farbname: «beige») Farbe und mit einem dicken braunen (Farbname: «brown») Rahmen.

Der Sandkasten hat eine Grösse von 400 x 400 Punkten. Er soll um unsere Startposition in der Mitte des Bildschirms (Ursprung) herum gezeichnet werden. Um das Quadrat zu zeichnen, müssen wir somit als erstes in eine Ecke gehen, ohne dabei einen Strich zu ziehen (penup(), goto(), pendown()) Beispielsweise die linke obere Ecke. Sie hat die Koordinate -200, 200.

# Sandkosten malen
# -  in linke ober Ecke gehen
penup()             
goto(-200,200)      
pendown()           

# - Farben und Stiftdicke definieren
fillcolor("beige")         
pencolor("brown")   
pensize(5)

# - Kasten zeichnen
begin_fill()
for k in range(4): 
    forward(400)
    right(90)
end_fill()                          

# zurück in die Mitte (nach Hause)
penup()
home()

Schildkröte vorbereiten

Bevor unsere Schildkröte loslegt, wollen wir noch einige kleine Einstellungen vornehmen.

# Schildkröte vorbereiten
shape("turtle")
pensize(1)
pencolor("black")
pendown()

Schildkröte laufen lassen

Nun geht’s los. Wir wollen unsere Schildkröte laufen lassen. Insgesamt soll sie 1000 Bewegungen machen. Hierzu definieren wir eine Zählschleife mit for und range(), so wie in Kapitel 7 vorgetellt. Die Schleife wiederholt die auf den Schleifenkopf (for i in range(1000): ) folgenden, eingeschobenen Anweisungen 1000 Mal.

# Spiel geht los
for i in range(1000):
    # Bewegung machen

Was soll die Schildkröte 1000 Mal machen? Wir lassen sie sich jeweils etwas drehen und anschliessend eine kurze Distanz geradeaus laufen. Damit es etwas spannender wird, dreht sie sich jeweils zufällig. Mit der Anweisung randint(von, bis) bzw. randint(-45, 45)  erzeugen wir eine zufällige Ganzzahl zwischen -45 und 45. Wir weisen diese der Variable dreh zu, welche wir anschliessend in der Drehbewegung right() verwenden. Analog definieren wir eine Zufallszahl zwischen 0 und 10, weisen diese der Variable dist zu, und bewegen uns entsprechend mit forward(dist) ein kleines Stück vorwärts.

# Spiel geht los

for i in range(1000):
    dreh = random.randint(-45, 45)
    right(dreh)
    dist = random.randint(0, 10)
    forward(dist)

Zurück zur Mitte

Als nächstes wollen wir sicherstellen, dass die Schildkröte wieder in der Mitte startet, sobald sie das Feld verlassen hat. Es gibt zahlreiche Möglichkeiten, wie man dies anstellen kann. Im Grunde genommen lesen wir jeweils die aktuelle Koordinate nach der Bewegung aus. Ist sie ausserhalb des Spielfelds, so muss die Schildkröte zurück zur Mitte. Zum auslesen der Koordinaten nutzen wir hier die Anweisungen xcor() und ycor().

Insgesamt gibt es 4 Möglichkeiten: Wir können oben, unten, links und rechts aus dem Sandkasten heraus.  

Wir ergänzen somit unseren Schleifenrumpf wie folgt:

for i in range(1000):
    dreh = random.randint(-45, 45)
    right(dreh)
    dist = random.randint(0, 10)
    forward(dist)

    if xcor() < -200:            # links aus dem Feld
        penup()
        home()
        pendown()
    if xcor()  > 200:            # rechts aus dem Feld
        penup()
        home()
        pendown()
    if ycor() < -200:             # unten aus dem Feld
        penup()
        home()
        pendown()
    if ycor() > 200:              # oben aus dem Feld
        penup()
        home()
        pendown()

Wichtig dabei sind die Einschübe. Das if Schlüsselwort gehört zum Schleifenrumpf und ist entsprechend eingeschoben. Die zu den if Anweisungen gehörenden Anweisungen sind noch mehr eingeschoben!

Als nächstes kürzen wir das Programm noch etwas ab. Anstelle 4 bedingter Anweisungsblöcke zu schreiben, packen wir alles in zwei Blöcke. Das sieht etwas schöner aus. Um jeweils 2 Bedingungen verknüpfen zu können, nutzen wir or (oder). Siehe hierzu nachfolgend Zeilen 8 und 13. Du kannst auch alles in einen einzelnen Block verpacken, wenn du es noch kürzer haben möchtest: 
if xcor() < -200 or xcor() > 200 or ycor() < -200 or ycor() > 200.

Zusätzlich möchten wir noch zählen, wie oft die Schildkröte zurück zur Mitte musste. Hierzu verwenden wir eine Variable und nennen sie fehler.  Bevor wir mit den 1000 Schritten beginnen, setzen wir den Wert der Variable fehler auf 0 (siehe nachfolgend Zeile 2). Jedes Mal, wenn die Schildkröte zurück zur Mitte geht, erhöhen wir den Wert um 1 (siehe nachfolgend Zeilen 9 & 14).

# Simulation
fehler = 0
for i in range(1000):
    dreh = random.randint(-45, 45)
    right(dreh)
    dist = random.randint(0, 10)
    forward(dist)
    if xcor() < -200 or xcor() > 200:
        fehler = fehler + 1
        penup()
        home()
        pendown()
    if ycor() < -200 or ycor() > 200:
        fehler = fehler + 1
        penup()
        home()
        pendown()

Resultat anzeigen

Damit haben wir es schon fast geschafft. Als letztes müssen wir nur noch das Resultat anzeigen:

# Resultat
stamp()
hideturtle()
penup()
goto(-200,-230)
write("Fehler: ", move=True)
write(fehler)

exitonclick()

In Zeile 2 machen wir mit der Anweisung stamp() einen Abdruck der Schildkröte. Damit sieht der Benutzer, wo die Schildkröte am Ende der Simulation war.

Anschliessend wollen wir noch die Punkte auf den Bildschirm schreiben. Wir verstecken die Schildkröte (Zeile 3), heben den Stift an (Zeile 4) und gehen an eine Position leicht unterhalb unseres Sandkastens (Zeile 5, nach x: -200, y: -230). Dort können wir anschliessend die Punktzahl hinschreiben.

Apropos Punktezahl: Im Beispielprogramm schreiben wir «Fehler…» anstelle «Punkte …». Du kannst dich für eine Version entscheiden. Wir können zählen, wie oft die Schildkröte erfolgreich das Spielfeld verlässt (dann sind es eher «Punkte»), oder wie oft sie aus dem Sandkasten «herausfällt» (dann sind es eher Fehler).

Vollständiger Code

Nachfolgend findest du den vollständigen Code der Grundversion. 

# Vorbereitun
# - Bibliotheken importieren
from turtle import *
import random

# - Bildschirm (Fenster) anlegen
setup(width= 600, height=700)
title("Punktesammler")
bgcolor("lightgrey")

# - Titel bzw. Willkommenstext
penup()
goto(0,230)
write("Willkommen zu unserer Simulation", align = "center", font = ("arial", 20, "normal"))
home()


# Sandkosten malen
# -  in linke ober Ecke gehen
penup()             
goto(-200,200)      
pendown()           

# - Farben und Stiftdicke definieren
fillcolor("beige")         
pencolor("brown")   
pensize(5)

# - Kasten zeichnen
begin_fill()
for k in range(4): 
    forward(400)
    right(90)
end_fill()                          

# - zurück in die Mitte (nach Hause)
penup()
home()


# Schildkröte vorbereiten
shape("turtle")
pensize(1)
pencolor("black")
pendown()

# Simulation
fehler = 0
for i in range(1000):
    dreh = random.randint(-45, 45)
    right(dreh)
    dist = random.randint(0, 10)
    forward(dist)
    if xcor() < -200 or xcor() > 200:
        fehler = fehler + 1
        penup()
        home()
        pendown()
    if ycor() < -200 or ycor() > 200:
        fehler = fehler + 1
        penup()
        home()
        pendown()

# Resultat
stamp()
hideturtle()
penup()
goto(-200,-230)
write("Fehler: ", move=True)
write(fehler)

exitonclick()

Erweiterungen

Unsere Simulation lässt sich beliebig erweitern. Der Phantasie sind kaum Grenzen gesetzt. Nachfolgend einige Beispiele:

Parameter

Eine erste Erweiterung wird sein, unser Programm zu parametrisieren. Soll heissen, dass man gewisse Wert in Variablen speichert. Beispielsweise die Anzahl Schritte, die maximale Drehung und maximale Distanz der Vorwärtsbewegung. Im nachfolgenden Codeausschnitt werden die drei Werte  in Zeile 6-8 festgelegt und anschliessend weiter unten an den entsprechenden Stellen eingesetzt (Zeile 12, 13, 15).

# Schildkröte vorbereiten
shape("turtle")
pensize(1)
pencolor("black")
pendown()
schritte = 1000
max_dreh = 45
max_dist = 10

# Simulation
fehler = 0
for i in range(schritte):
    dreh = random.randint(-max_dreh, max_dreh)
    right(dreh)
    dist = random.randint(0, max_dist)
    forward(dist)
    if xcor() < -200 or xcor() > 200:
        fehler = fehler + 1
        penup()
        home()
        pendown()
    if ycor() < -200 or ycor() > 200:
        fehler = fehler + 1
        penup()
        home()
        pendown()

Sind gewisse Werte erst einmal parametrisiert, so kann man mit dem Programm leicht herumexperimentieren. Oder man kann den Benutzer am Anfang fragen, welche Werte verwendet werden sollen. Hier gilt es dann ein besonderes Augenmerk auf die Datentypen zu werfen. numinput() liefert immer eine Zahl vom Datentyp float() zurück. Die Anweisungen range() benötigt aber eine Ganzzahl. Man muss daher die Anzahl Schritte mit int() in eine Ganzzahl umwandeln.

schritte = int(numinput("Schritte", "Wie viele Schritte", default=1000, minval=50, maxval=10000))
max_dreh = numinput("Drehung", "Drehwinkel +/-", default=45, minval=1, maxval=360)
max_dist = numinput("Distanz", "max. Distanz", default=10, minval=1, maxval=50)

Selbstverständlich kann man weitere Werte parametrisieren. Beispielsweise die Grösse des Sandkastens.

Farben

Nachfolgend ein kleines Beispiel zu den Farben:

# Simulation
fehler = 0
colormode(255)
for i in range(schritte):
    dreh = random.randint(-max_dreh, max_dreh)
    right(dreh)
    dist = random.randint(0, max_dist)
    pencolor(100,200,int(250*dist/max_dist))
    forward(dist)
    if i %100 == 0:
        dot(5, "red")
    if xcor() < -200 or xcor() > 200:
        fehler = fehler + 1
        penup()
        home()
        pendown()
        # ....  

Comments