Dies ist eine alte Version des Dokuments!


Minecraft mit Hilfe von Python steuern

Minecraft kann durch Programme gesteuert werden. Dabei kann die Programmierung mit Blöcken (wie in Scratch) oder durch Text-Programmierung (Python oder JavaScript) erfolgen.

Um in Minecraft Education programmieren zu können drückt man die Taste c. Tut man dies zum ersten Mal, so kann man den gewünschten Editor wählen. Am besten ist hier die Wahl „Make Code“, da man hier die freie Auswahl zwischen Blockprogrammierung, Python oder JavaScript hat. Wir werden Python für die Programmierung verwenden.

Grundlagen und vordefinierte Befehle

Bereich ausklappen/einklappen

Absulute und relative Koordinaten

Man kann Positionen auf drei Arten angeben: absolut, relativ zur Befehlsquelle (Agent oder Spieler) bezüglich der Himmelsrichtung und relativ zur Befehlsquelle bezüglich der Blickrichtung.

Befehl Bedeutung
world(x,y,z) Absolute Position in der Welt
pos(x,y,z) Relativ zum Spieler/Agenten bez. Himmelsrichtung
local_pos(x,y,z) Relativ zum Spieler/Agenten bez. Blickrichtung

Der Agent

Neben der Spielfigur gibt es in Minecraft Education auch einen sogenannten Agenten. Dabei handelt es sich um einen kleinen Roboter, den man mit den Agent-Befehlen steuern kann. Man kann dem Agenten Befehle erteilen und ihn dazu bringen, sich zu bewegen oder zu bauen. Der Agent hat sein eigenes Inventar, das man per Rechtsklick auf den Agenten einsehen und verändern kann. Man kann das Inventar auch durch den Befehl agent.set_item verändern (siehe weiter unten).

Den Agenten bauen lassen, oder direkt bauen

Man kann nun den Agenten dazu bringen, etwas für uns zu bauen, oder man kann auch direkt bauen (ohne die Verwendung der Agenten). Die folgenden beiden Programme setzen jeweils einen Gold- und einen Diamantblock an die absoluten Positionen (0,-60,10) und (0,-60,11):

blocks.place(GOLD_BLOCK,world(0,-60,10))
blocks.place(DIAMOND_BLOCK, world(0,-60,11))

agent.set_item(GOLD_BLOCK,1,1) # 1 Goldblock ins Fach 1 legen 
agent.set_item(DIAMOND_BLOCK,1,2) # 1 Diamantblock ins Fach 2 legen
agent.teleport(world(0,-60,11), SOUTH) # zur Position teleportieren
agent.set_slot(1)
agent.place(BACK) # Block aus dem ausgewählten Fach hinter sich platzieren
agent.move(FORWARD,1)
agent.set_slot(2)
agent.place(BACK)

Die definierten Befehle für den Agenten bzw. für die Blöcke findest du am besten, indem du in MakeCode in der linken Leiste einen Bereich wählst (Grundlagen, Spieler, Blöcke…) und dann den entsprechenden Befehl ins Code-Fenster ziehst.

Praktische Option, wenn man den Agenten bauen lässt (assist Es kann praktisch sein, die Option assist zu setzen, wenn man mit dem Agenten baut. Dadurch kann man erreichen, dass der Agent bei jeder Bewegung aus dem aktiven Fach baut (dadurch muss man nicht jedes Mal agent.place(…) aufrufen) bzw. dass der Agent alle Hindernisse zerstört, die sich ihm in den Weg stellen.

agent.set_assist(PLACE_ON_MOVE, [TRUE/FALSE]) Bauen, während Agent sich bewegt (Ja/Nein)
agent.set_assist(DESTROY_OBSTACLES, [TRUE/FALSE]) Hindernisse zerstören
agent.set_assist(PLACE_FROM_ANY_SLOT, [TRUE/FALSE]) Automatisch nächstes Fach wählen, wenn ein Fach leer ist

Achtung: Wenn man im Kreativmodus ist, so ist das Material unlimitiert, d.h. es nimmt im Inventar des Spielers oder des Agenten nicht ab, wenn man baut.

1. Variablen

Bereich ausklappen/einklappen

Eine Variable ist ein Behälter, in dem Daten gespeichert werden können. Stelle dir z.B. einen Koffer vor, in dem du etwas aufbewahren kannst. Die Variable ist der Koffer und der Wert der Variablen ist der Inhalt dieses Koffers. Eine Variable erstellt man mit dem Gleichheitszeichen:

 meinKoffer = 3 

1.1 Dynamische Typisierung (Python) vs. statische Typisierung

Evtl. kennst du aus anderen Programmiersprachen die Regel, dass man Variablen vor ihrem ersten Gebrauch deklarieren muss. Dabei muss man den Namen und ihren Typ angeben, z.B. int a = 3 (in C#). Dadurch weiss der Compiler/Interpreter direkt, um welchen Typ von Variaben es sich handelt (hier eine ganze Zahl) und er kann ensprechend Platz im Speicher reservieren. Diese Variable kann danach aber nur den angegebenen Typ annehmen. Solche Programmiersprachen sind statisch typisiert. In Python ist dies nicht so: du kannst Variablen einfach definieren, indem du einem Variablennamen einen Wert zuweist. In derselben Variablen kannst du dann ganze Zahlen, Kommazahlen, Text oder auch komplexere Objekte (Listen etc.) speichern. Python ist dynamisch typisiert.

a = 1           # eine ganze Zahl (integer)
print(a+1)
a = "Ein Text"  # Text (string)
a = 2.13        # Kommazahl (float)
a = True        # Wahrheitswert (boolean)

1.2 Namen für Variablen und Funktionen

Wichtige Variablen oder Funktionen sollten (wenn möglich) Namen haben, die ihre Bedeutung erkennen lassen. Es gibt zwei Konventionen, die sich bei Namen mit mehreren Wörter etabliert haben:

Camelcase Bei Namen, die aus mehreren Wörtern bestehen, wird jeweils bei einem neuen Wort der erste Buchstabe grossgeschrieben. Der allererste Buchstabe jedoch klein (Bsp: anzahlGoldbloecke, rechtesFenster).
Pascalcase Exakt dasselbe wie Camelcase, jedoch wird auch der erste Buchstabe gross geschrieben (Bsp: AnzahlGoldbloecke, RechtesFenster).
Snakecase Bei Namen, die aus mehreren Wörtern bestehen, werden diese durch einen Unterstrich getrennt (Bsp. anzahl_goldbloecke, rechtes_fenster)

1.3 Was bringen Variablen?

  • Wenn man in einem Programm sehr häufig denselben Wert verwendet, kann man diesen in einer Variablen (oder Konstanten) speichern und dann im Programm diese Variable verwenden. Will man den Wert ändern, so kann man diesen zentral am Anfang bei der Variablendefinition ändern und die Ändreung wird dann überall im Programm aktiv.
  • Häufig weiss man bei der Erstellung des Programms gar noch nicht, welchen Wert gewisse Daten haben (z.B. muss der Benutzer die Anzahl der Spieler eingeben, oder sein Alter etc.). Einer Variablen kann man dann zur Laufzeit des Programms den entsprechenden Wert zuweisen.

Zusammenfassend lässt sich sagen, dass durch Variablen die Daten in den Programmen flexiebler einsetzbar und besser anpassbar werden.

Achtung, auch wenn das Konzept der Variablen hier sehr banal und einleuchtend erscheinen mag, ist es sehr zentral und es steckt einiges mehr dahinter, als man vielleicht vermuten könnte. Wir werden uns die etwas tiefergehenden Konzepte weiter unten genauer anschauen (lokale/globale Variable, mutable/imutable, Referenz vs. Value)

2. Eigene Funktionen definieren mit ''def''

Bereich ausklappen/einklappen

Funktion oder Prozedur (die beiden Begriffe werden hier als Synonym betrachtet) bezeichnet ein Unterprogramm, das durch seinen Namen aufgerufen werden kann. Es gibt viele vordefinierte Funktionen in Python oder Minecraft (z.B. pow(a,b), agent.turn(LEFT)) aber sehr oft ist es sinnvoll, auch eigene Funktionen zu programmieren. Der Vorteil liegt auf der Hand: wenn mehrmals dieselben Anweisungen ausgeführt werden sollen, kann man diese als Funktion speichern und dann mehrmals aufrufen. Dadurch wird Redundanz (sich wiederholender, überflüssiger Code) vermieden und das Progamm wird strukturiert.

Funktionen erstellt man in Python durch den Befehl def. Wir können beispielsweise eine Funktion definieren, welche den Agenten eine Spirale bauen lässt und diese dann mehrmals aufrufen:


agent.set_assist(PLACE_ON_MOVE, True) # Agent baut, wenn er sich bewegt
agent.set_assist(DESTROY_OBSTACLES,True) # Agent zerstört Hindernisse beim Bauen
agent.set_item(SANDSTONE,64,1) # 64 Blöcke Sandstein ins erste Fach des Inventars legen

def baueSpirale():
    drehRichtung = LEFT # Alternativ: drehRichtung = RIGHT
    agent.move(FORWARD,1)
    agent.turn(drehRichtung)
    agent.move(FORWARD,2)
    agent.turn(drehRichtung)
    agent.move(FORWARD,3)
    agent.turn(drehRichtung)
    agent.move(FORWARD,4)
    agent.turn(drehRichtung)
    agent.move(FORWARD,5)
    agent.turn(drehRichtung)
    agent.move(FORWARD,6)

baueSpirale()
agent.move(FORWARD, 10)
baueSpirale()
agent.move(FORWARD, 10)
baueSpirale()
agent.move(FORWARD, 10)
baueSpirale()
agent.move(FORWARD, 10)

Aufgaben A

  1. Überlege dir, was der Agent genau baut, wenn man das oben angegebene Programm ausführt. Bei welcher Zeile des Codes beginnt der Agent mit dem Bauen?
  2. Überprüfe deine Überlegung, indem du das Programm in Minecraft einfügst und ausführst (evt. musst du zunächst den Agenten zu dir teleportieren. Im Chat kannst du das machen mit /teleport @c @s)
  3. Das angegebene Programm ist nicht sehr optimiert - es enthält viel Redundanz (überflüssiger, sich wiederholender Code) und es ist nicht gut anpassbar. Schreibe die Funktion um, so dass sie kürzer und eleganter wird (verwende dazu die unten erklärten Konzepte, z.B. einen Parameter in welchem man wählen kann, ob sie rechts- oder linksdrehend ist und eine for-Schleife)

Globale und lokale Variablen

Oben wird die Variable drehRichtung innerhalb der Funktion baueSpirale definiert. Man kann sie dann nur innerhalb dieser Funktion verwenden: man spricht von einer lokalen Variablen. Würde man die Variable ausserhalb der Funktion (ganz zu Beginn des Codes) definieren, dann wäre sie überall im Programm sichtbar, auch in den Funktionen. Man spricht dann von einer globalen Variable.

Globale Variablen kann man innerhalb einer Funktion nur ändern, wenn man dies in der Funktion mit dem Schlüsselwort global deklariert.

Wenn man mit def eine eigene Funktion definiert, kann man diese flexibler machen, indem man Parameter verwendet. Dies sind Variablen, die beim Aufruf der Funktion übergeben werden und dann innerhalb der Funktion wie lokale Variablen funktionieren. So könnte man z.B. eine Funktion Hochhaus(n) definieren, welches ein Hochhaus mit n Etagen baut. Wenn man die Funktion dann aufruft, muss man für n einen konkreten Wert angeben: z.B. Hochhaus(7).

Das Beispiel unserer Spirale können wir mit Parametern etwas eleganter gestalten: wir übergeben der Funktion die Drehrichtung als Parameter.

agent.set_assist(PLACE_ON_MOVE, True) # Agent baut, wenn er sich bewegt
agent.set_assist(DESTROY_OBSTACLES,True) # Agent zerstört Hindernisse beim Bauen
agent.set_item(SANDSTONE,64,1) # 64 Blöcke Sandstein ins erste Fach des Inventars legen

def baueSpiraleNeu(richtung):
    agent.move(FORWARD,1)
    agent.turn(richtung)
    agent.move(FORWARD,2)
    agent.turn(richtung)
    agent.move(FORWARD,3)
    agent.turn(richtung)
    agent.move(FORWARD,4)
    agent.turn(richtung)
    agent.move(FORWARD,5)
    agent.turn(richtung)
    agent.move(FORWARD,6)

baueSpiraleNeu(TurnDirection.LEFT)
agent.move(FORWARD, 10)
baueSpiraleNeu(TurnDirection.RIGHT)
agent.move(FORWARD, 10)
baueSpiraleNeu(TurnDirection.LEFT)
agent.move(FORWARD, 10)
baueSpiraleNeu(TurnDirection.RIGHT)
agent.move(FORWARD, 10)
Nun können wir dieselbe Funktion verwenden, um rechtsdrehende und linksdrehende Spiralen zu bauen. Wir übergeben beim Aufruf der Funktion die Drehrichtung (baueSpiraleNeu(TurnDirection.Right)) und diese Drehrichtung ist dann innerhalb der Funktion an die Variable richtung gekoppelt (da wir bei der Definition der Funktion diesen Namen gewählt haben).

Die bisher betrachteten Funktionen hatten keinen Rückgabewert (in einigen Büchern werden diese als Prozeduren bezeichnet). Eine Funktion kann jedoch auch einen Wert zurückgeben und dieser kann dann im Programm weiter verwenden und z.B. in einer Variablen gespeichert werden.

Im Minecraft hat z.B. die Funktion agent.get_orientation() einen Rückgabewert (die Blickrichtung des Agenten). Dadurch kann man z.B. schreiben:

direction1 = agent.get_orientation()

if direction1 == -90:
    agent.turn(RIGHT)
Natürlich könnte man auch auf die Speicherung in der Variablen verzichten und direkt in der if-Abfrage die Orientierung verwenden:
if agent.get_orientation() == -90:
    agent.turn(RIGHT)

Wenn man eigene Funktionen mit Rückgabewert definiert, so verwendet man das Schlüsselwort return. Sobald man einen Wert zurückgegeben hat, machen weitere Befehle in der Funktion keinen Sinn mehr, da diese ignoriert werden:

from math import sqrt

def mitterNachtsFormel(a,b,c):
    d = b**2-4*a*c
    if d < 0:
        return False  # False zurückgeben, da es keine Lösung gibt 
    else:
        return [(-b+sqrt(d))/(2*a),(-b-sqrt(d))/(2*a)] # Liste mit den beiden Lösungen zurückgeben

Funktionen können auch rekursiv definiert werden, d.h. sie rufen sich selbst auf. Beispiele:

def rec1(n):
    if n<=1: 
        return
    blocks.fill(RED_WOOL,pos(0,0,0),pos(n,0,n))
    player.teleport(pos(1,1,1))
    rec1(n-2)

rec1(10)
def rec2(x,y):
    if x<=1 or y>=30:
        blocks.place(GOLD_BLOCK,pos(x,y,10))
    else:
        blocks.place(GRANITE, pos(x,y,10))
        rec2(x-1,y+2)
        rec2(x+1, y+1)

rec2(30,4)

Aufgaben B

  1. Überlege dir, was der Agent genau baut, wenn man das oben angegebene Programm rec1 ausführt. Wie könnte man das Programm ohne Rekursion programmieren?
  2. Welcher fundamentale Unterschied besteht zwischen den beiden Programmen? Wie wirkt sich dies auch den Zeit- und Speicherbedarf aus?

3. Weiterführende Überlegungen zu Variablen und Parametern

Bereich ausklappen/einklappen

Wenn man an die Deklaration von Variablen denkt, so stellt man sich intuitiv eine Tabelle vor, in welcher der entsprechende Name der Variable an ihren Wert gebunden ist:

a = 10
b = 13.2
name1 = "Hans Meier"

Variablenname Wert
a 10
b 13.2
name1 „Hans Meier“

Doch dies ist nur die halbe Wahrheit. Betrachte die beiden folgenden Programme und überlege dir, welchen Wert am Schluss b bzw. list2 haben. Führe die Befehle dann z.B. in Thonny aus. Was stellst du fest? Wie könnte man sich das überraschende Resultat erklären?

a = 3  
b = a
a = 5 # a ändern
print(b)
Was denkst du wird am Schluss angezeigt für den Wert von b? Verwenden wir statt ganzer Zahlen eine Liste:
list1 = [1,2,3]
list2 = list1
list1.append(20) #  die Zahl 20 zur Liste list1 hinzufügen
print(list2)

Nun ändern wir den Wert der Variablen list1 anders:

list1 = [1,2,3]
list2 = list1
list1 = [100,101]
print(list2)

Aufgaben C

  1. Erkläre den Unterschied, von call by value und call by reference. Hier ist das globale Konzept in der Programmierung gemeint, nicht die konkrete Umsetzung in Python.
  2. Was bedeutet es, wenn eine Variable mutable bzw. immutable ist. Welche Variablentypen sind in Python mutable bzw. immutable?
  3. „In Python sind alle Variablen Objekte (d.h. Referenztypen) und wenn sie als Parameter einer Funktion übergeben werden, wird ihre Referenz by value übertragen.“ Erkläre diese Aussage.

3.1.1 Call by value

Call by value bedeutet, dass der Wert der Variablen verwendet wird, wenn man diese aufruft, oder als Parameter einer Funktion übergibt. Ruft man also eine Funktion auf, so wird der Wert des Argumentes übergeben:

my_number = 5

def add_one(number):
   number = number + 1

add_one(my_number) # der WERT von my_number wird übergeben
print(my_number)  
Im Programm von oben, wird add_one mit dem Parameter my_number aufgerufen. Da dieser Aufruf by Value, d.h. über den Wert erfolgt, wird der Wert von my_number (also 5) als Argument übergeben und an den Parameter number gebunden. Wenn man dann diese Variable number um 1 vergrössert. Hat dies keinen Einfluss auf die Variable my_number: sie verbleibt auf dem Wert 5.

3.1.2 Call by reference

Beim Konzept call by reference wird nicht der Wert der Variablen gespeichert oder übergeben, sondern ihre Referenz , d.h. ihr Ort im Speicher. Wenn man in Python id(<Variablenname>) aufruft, dann erhält man eine Zahl, welche für die Adresse der Variablen im Speicher steht. Man muss sich dann die Variablen so vorstellen:

list1 = [1,2,3]
list2 = list1

Variablenname Adresse Wert an der Adresse
list1 0x1f34 [1,2,3]
list2 0x1f34 [1,2,3]

Da in der Variablen list1 die Referenz (d.h. die Adresse) gespeichert ist und diese dann auf die Liste list2 übertragen wird, zeigen beide auf denselben Ort im Speicher. Wird nun die Liste list1 geändert durch list1.append(20), dann wird die Liste an dieser Adresse geändert und beide Variablen (list1 und list2) zeigen auf diese neue Liste [1,2,3,20].

Wenn man Parameter als Referenz übergibt, dann ändern sich übergebenen Variablen, wenn man diese in einer Funktion ändert. Das folgende ist ein theoretisches Beispiel (in Python ist dies so nicht möglich, siehe die Erklärung unten):

my_number = 5

def add_one(byRef number):  # theoretisches Beispiel: Referenz von number wird übergeben
   number = number + 1

add_one(my_number)  # Referenz (Adresse) von my_number wird übergeben.
print(my_number)  # my_number wäre nun um 1 vergrössert worden, also auf 6.

Würde der Parameter über die Referenz übergeben, würde im obigen Beispiel die Variable my_number auf 6 geändert.

In vielen Programmiersprachen werden die Grunddatentypen (Integer, Float, Boolean, String) byValue gespeichert und übergeben und die komplexeren Datentypen (Arrays, Objekte, Dictionaries) als Referenz. Nicht so in Python. Alle Variablen sind Objekte und demensprechend Referenztypen (d.h. die Referenz wird gespeichert). Aber die primitiven Datentypen (alle Zahltypen, Boolean, Strings, Tuples) sind immutable, d.h. man kann sie nicht ändern sondern nur durch ein neues Objekt überschreiben. Komplexere Datentypen (z.B. Listen, Dictionaries, eigene Objekte) sind mutable, d.h. man kann sie direkt an ihrem Speicherort ändern. Es folgen die drei Beispielprogramme von oben mit der entsprechenden Erklärung für Python:

Wenn die Variable immutable ist, verhält sich Python wie Call by Value

a = 3  # in a wird die Referenz zum Integer-Objekt 3 gespeichert
b = a  # b zeigt auf die Adresse desselben Integer-Objekts mit der Zahl 3
a = 5  # da a immutable ist, wird ein neues Integerobjekt erstellt und a zeigt auf dessen Adresse
print(b) # b zeigt immer noch auf die Adresse des Integerobjektes mit der Zahl 3

Wenn die Variable mutable ist, verhält sich Python wie Call by Reference

list1 = [1,2,3]  # in list1 die Adresse zur Liste [1,2,3] speichern
list2 = list1    # list2 zeigt auf dieselbe Adresse wie list1
list1.append(20) # list1 ist mutable und wird an Ort wird list1 zu [1,2,3,20] geändert
print(list2)     # list2 zeigt auf dieselbe Adresse wie list1, d.h. list2 ist auch [1,2,3,20]

Wenn ein neues Objekt mit einer neuen Adresse erstellt wird:

list1 = [1,2,3]  # in list1 die Adresse zur Liste [1,2,3] speichern
list2 = list1    # list2 zeigt auf dieselbe Adresse wie list1
list1 = [100,101] # list1 wird nicht an Ort geändert. Ein neues Listenobjekt mit neuer Adresse wird erstellt.
print(list2)     # list2 zeigt immer noch auf die Adresse in der [1,2,3] steht

Aufgabe D

  1. Betrachte das untenstehende Programm und erkläre Zeile für Zeile, was genau geschieht.
  2. Was wird am Schluss in l1 bzw. l2 stehen?

def change_value(list1, list2):
    list1[2] = 5
    list2 = [6, 10]
    
l1 = [1,2,3,4]
l2 = [10,11,12]

change_value(l1,l2)

print(l1)
print(l2)

4. Listen (Arrays) erstellen, durchgehen und der range-Befehl

Bereich ausklappen/einklappen

Mit eckigen Klammern kann man in Python eine Liste erstellen. Dabei können die einzelnen Elemente der Liste ganz unterschiedliche Typen haben. Es können sogar selbst wieder Listen sein. Auf die einzelnen Elemente der Liste kann man zugreifen, indem man den Namen der Liste mit eckigen Klammern und dem Index angibt (der Index bezeichnet die Position des Elementes in der Liste - er beginnt bei 0. Gibt man als Index negative Zahlen an, so zählt Python vom letzten Element an rückwärts. D.h. das letzte Element der Liste Liste1 erhält man mit Liste1[-1]

>>> Liste1 = [1, 2, 3, 4, 5]
>>> Liste2 = [51, "Hallo", True, 3.141592]
>>> Liste1[1]
2
>>> Liste2[-1]
3.141592
>>> 

Listen verwendet man oft, wenn man viele Daten speichern muss. Statt dass man z.B. zehn Variablen v1=5, v2=3, v3=8, v4=9,…, v10=0.34 definiert, erstellt man besser eine Liste 'v = [5,3,8,9,…,0.34]' und greift dann auf die Werte zu, indem man v[0], v[1] etc. aufruft.

Aufgabe E

Gegeben ist eine Liste von Baumaterialien: Materialien=[RED_SANDSTONE, GRASS, WOOL, DIAMOND_BLOCK, GOLD_BLOCK]

Schreibe ein Programm, welches den Agenten dazu bringt, eine Linie mit fünf verschieden Blöcken aus den Materialien der Liste zu bauen. Die Reihenfolge der Liste soll dabei eingehalten werden.

Tipps:

  • Dein Code beginnt mit Materialien=[RED_SANDSTONE, GRASS, WOOL, DIAMOND_BLOCK, GOLD_BLOCK]
  • Die Liste muss in deinem Code verwendet werden
  • Dein Programm muss zunächst das Inventar des Agenten mit den richtigen Materialien füllen.

Sehr häufig möchte man eine regelmässige Liste von Zahlen erstellen: z.B. alle Zahlen von 1 bis 10 oder die Zahlen von 1 bis 100 in 2er-Schritten etc. Dazu ist der range-Befehl sehr praktisch: Er erstellt eine Liste von einem Startwert bis zu einem Endwert mit einer bestimmten Schrittweite:

range-Befehl Bedeutung
range(10) Liste der Zahlen von 0 bis 10 (exklusive 10), also [0,1,2,3,4,5,6,7,8,9]
range(4,9) Liste der Zahlen von 4 (inkl.) bis 9 (exkl.), also [4,5,6,7,8]
range(13,25,3) Liste der Zahlen von 13 (inkl.) bis 25 (exkl.) in 3er-Schritten, also [13,16,19,22]

Aufgabe F

Erstelle mit dem range-Befehl die folgenden Listen:

  1. L1 die Liste der ganzen Zahlen zwischen 0 und 8.
  2. L2 die Liste der ganzen Zahlen, die kleiner als 20 und durch 3 teilbar sind.
  3. L3 die Liste der ganzen Zahlen zwischen 20 und 40, die durch 4 teilbar sind.

Tipp: Mit dem Befehl player.say kannst du deine Listen anzeigen lassen. Beispiel: player.say(L1)

Ganz oft will man für alle Elemente in einer Liste dasselbe tun, d.h. man will die Liste „abklappern“ (durchgehen) und dann für jedes Element dieselbe Aktion durchfühen. Z.B. hat man eine Liste von Positionen und möchte bei jeder Position ein Haus bauen, oder man hat eine Liste von Baumaterialien und man möchte für jedes dieser Materialien einen Block platzieren etc.

Dafür gibt es in Python den Befehl for <Variable> in <Liste>. Am besten versteht man dies an einem Beispiel. Wir verbessern unser Programm, welches die Spirale baut, weiter:

def baueSpirale(richtung):    
    agent.move(FORWARD,1)
    agent.turn(richtung)
    agent.move(FORWARD,2)
    agent.turn(richtung)
    agent.move(FORWARD,3)
    agent.turn(richtung)
    agent.move(FORWARD,4)
    agent.turn(richtung)
    agent.move(FORWARD,5)
    agent.turn(richtung)
    agent.move(FORWARD,6)

Wir sagen Python einfach: Gehe alle Zahlen von 1 bis 6 durch (sage der Variablen z.B. zahl) und mache dann die beiden Befehle:

    agent.move(FORWARD,zahl)
    agent.turn(richtung)

Damit sieht unser Programm viel kürzer aus:

def baueSpirale(richtung):
    for zahl in [1,2,3,4,5,6]:
        agent.move(FORWARD,zahl)
        agent.turn(richtung) 

Die Liste [1,2,3,4,5,6] können wir uns natürlich auch mit dem Range-Befehl erstellen lassen:

def baueSpirale(richtung):
    for zahl in range(1,7):
        agent.move(FORWARD,zahl)
        agent.turn(richtung) 
Will man z.B. etwas in Minecraft 10-Mal wiederholen, so kann man einfach den Befehl: for i in range(10) verwenden.
for i in range(10): # i geht die Zahlen von 0 bis 9 durch
   agent.move(FORWARD,2)
   agent.place(BACK) 

Listenbefehl Bedeutung
L1 = [3, „bla“, 5, 6] Liste erstellen
L1[1] Auf das erste Element zugreifen (Achtung: Zählung beginnt bei 0!)
len(L1) Länge der Liste L1 (Anzahl der Elemente)
[3,6]+[1,9] oder
[3,6].extend([1,9])
Listen zusammenfügen
L1.append(3) Element am Schluss der Liste anfügen
del L1[0] Ein Element bei einem bestimmten Index löschen
L1.remove(„bla“) Ein Element mit einem bestimmten Wert löschen
L1[1:3] Unterliste von Index 1 (inkl.) bis Index 3 (exkl.)
L1[1:] Unterliste von Index 1 (inkl.) bis zum Schluss der Liste
3 in L1 Testet, ob der Wert 3 in der Liste L1 enthalten ist. Gibt True oder False zurück.

Aufgaben G

  1. Schreibe eine Funktion LinieDuo(n), welche eine Linie mit zwei selbst gewählten Materialien baut, wobei es jeweils abwechselt.
  2. Schreibe eine Funktion LinieTre(n), welche eine Linie mit drei selbst gewählten Materialien baut, wobei es jeweils abwechselt mit allen drei Materialien.
  3. Schreibe eine Funktion LinieDoppelt(n), welche eine Linie mit Höhe zwei Blöcke und Länge n baut. Das Baumaterial ist frei wählbar.
  4. Schreibe eine Funktion LinieMitHöhe(n,h), welche eine Linie mit Länge n und Höhe h baut.
  5. Schreibe eine Funktion quadrat(n), welche den Agenten dazu bringt, ein nicht ausgefülltes Quadrat der Länge n zu bauen.
  6. Schreibe eine Funktion quadratf(n) welche ein gefülltes Quadrat baut.
  7. Schreibe eine Funktion tower(n,h) welche einen Turm mit quadratischer Grundfläche der Breite n und der Höhe h baut.

5. Verzweigungen mit ''if'' und Schleifen mit ''while''

Bereich ausklappen/einklappen

Mit der if Anweisung kann man Befehle nur dann ausführen lassen, wenn eine bestimmte Bedingung wahr (True) ist.

if (agent.get_item_count(1)>0):  # wenn noch Material im Fach 1 liegt
    agent.set_slot(1)   # Das aktuelle Fach auf 1 setzen

Man kann mit dem if-Befehl auch mehrere Bedingungen prüfen (elif) und am Schluss etwas ausführen, wenn keine Bedingung zutrifft (else):

if (agent.get_item_count(1)>0): # wenn Material im Fach 1 liegt
    agent.set_slot(1)     # Fach 1 als aktuelles Fach setzen
elif (agent.get_item_count(2)>0):  # sonst: wenn Material in Fach 2 liegt
    agent.set_slot(2)  # Fach2 als aktuelles Fach setzen
else: # sost
    player.say("Ich habe nichts im Fach 1 oder 2!") # den Spieler sagen lassen, dass nichts im Inventarslot 1 oder 2 liegt.

Wir haben bereits die for-Schleife gesehen, um Befehle zu wiederholen. Mit der while-Schleife kann man das auch tun, wobei die Wiederholung nicht durch eine Liste gesteuert ist (wie bei der for-Schleife) sondern durch eine Bedingung. Solange die Bedinugung wahr (True) ist, werden die Befehle wiederholt (das folgende Beispiel funktioniert im Kreativ-Modus nicht, da dort die Anzahl der Materialien nicht abnimmt):

while agent.inspect(AgentInspection.BLOCK, FORWARD)==AIR:  # solange AIR vor ihm ist
    agent.move(FORWARD, 1)

while agent.detect(AgentDetection.BLOCK, FORWARD):  # solange ein Block for ihm ist
    agent.turn_left()

Aufgaben E

  1. Erstelle eine Funktion linie(n, material), welche eine Linie der Länge n mit dem übergebenen Material erstellt.
  2. Erstelle eine Funktion linie2(n, material1, material2), welche eine Linie der Länge n mit abwechselnden Materialien material1 und material2 macht.
  3. Erstelle eine Funktion rechteck(n,m,material) welche ein Rechteck der Länge n und Breite m baut mit dem Material material
  4. Erstelle eine Funktion schachbrett(n,m,mat1,mat2), welche ein Schachbrett der Länge n und Breite m macht mit den Materialien mat1 und mat2
  5. Erstelle eine Funktion scan(), welche den Bereich (0,-60,0) bis (20,-60,20) abklappert, alle gefundenen Blöcke zerstört und speichert und dann dasselbe an der Position (100,-60,0) bis (100,-60,20) baut.
  6. Schreibe eine Funktion kugel(n), welche eine Kugel baut mit „Radius“ n, ohne dabei die Funktion shapes.sphere vom Code-Builder zu nutzen.
  7. Schreibe eine Funktion house(…), welche ein Haus baut.!

Zurück zur Übersicht

  • ef/grundlagen_python.1725363217.txt.gz
  • Zuletzt geändert: 2024/09/03 13:33
  • von lehmannr