Seite anzeigenÄltere VersionenLinks hierherNach oben Diese Seite ist nicht editierbar. Sie können den Quelltext sehen, jedoch nicht verändern. Kontaktieren Sie den Administrator, wenn Sie glauben, dass hier ein Fehler vorliegt. ====== Minecraft mit Hilfe von Python steuern ====== ===== Minecraft per Code 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. <callout type="primary" title="Grundlagen und vordefinierte Befehle"> {{fa>angle-double-down}}<button collapse="grundlagen" type="link">Bereich ausklappen/einklappen </button> <collapse id="grundlagen"> ** 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 | | ''pos_local(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): <sxh python;title:Blöcke direkt bauen> blocks.place(GOLD_BLOCK,world(0,-60,10)) blocks.place(DIAMOND_BLOCK, world(0,-60,11)) </sxh> <sxh python;title:Mit Hilfe des Agenten bauen> 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) </sxh> 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: Im Kreativmodus ist das Material unlimitiert, d.h. das Material nimmt im Inventar des Spielers oder des Agenten nicht ab, wenn man baut. </collapse> </callout> ===== Programmierkonzepte in Python ===== <callout type="primary" title="1. Variablen"> {{fa>angle-double-down}}<button collapse="collvar" type="link">Bereich ausklappen/einklappen </button> <collapse id="collvar"> 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: <sxh python> meinKoffer = 3 </sxh> ** 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**. <sxh python;title:In einer Variablen können verschiedene Datentypen gespeichert werden> a = 1 # eine ganze Zahl (integer) print(a+1) a = "Ein Text" # Text (string) a = 2.13 # Kommazahl (float) a = True # Wahrheitswert (boolean) </sxh> ** 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. <well>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)</well> </collapse> </callout> <callout type="info" title="2. Eigene Funktionen definieren mit ''def''"> {{fa>angle-double-down}}<button collapse="collfunc" type="link">Bereich ausklappen/einklappen </button> <collapse id="collfunc"> ==== 2.1 Funktionen/Prozeduren (ohne Parameter und Rückgabewert) ==== **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: <sxh python;title:Eine Funktion, welche eine Spirale baut> 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) </sxh> <WRAP round todo 80%> **Aufgaben A** - Ü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? - Ü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'') - 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) </WRAP> <well> ** 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**. <color #bf3b53>Globale Variablen kann man innerhalb einer Funktion nur ändern, wenn man dies in der Funktion mit dem Schlüsselwort </color> ''global'' <color #bf3b53>deklariert.</color> </well> ==== 2.2 Funktionen mit Parametern ==== 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. <sxh python;title:Spiral-Funktion mit Parameter;highlight:[5,18]> 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) </sxh> 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). ==== 2.3 Funktionen mit Rückgabewert ==== 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: <sxh python> direction1 = agent.get_orientation() if direction1 == -90: agent.turn(RIGHT) </sxh> Natürlich könnte man auch auf die Speicherung in der Variablen verzichten und direkt in der if-Abfrage die Orientierung verwenden: <sxh python> if agent.get_orientation() == -90: agent.turn(RIGHT) </sxh> 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: <sxhpython> 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 </sxh> ==== 2.4 Rekursive Funktionen ==== Funktionen können auch rekursiv definiert werden, d.h. sie rufen sich selbst auf. Beispiele: <sxh python;title:Eine rekursive Funktion> 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) </sxh> <sxh python;title:eine weitere rekursive Funktion> 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) </sxh> <WRAP round todo 80%> **Aufgaben B** - Ü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? - Welcher fundamentale Unterschied besteht zwischen den beiden Programmen? Wie wirkt sich dies auch den Zeit- und Speicherbedarf aus? </WRAP> </collapse> </callout> <callout type="danger" title="3. Weiterführende Überlegungen zu Variablen und Parametern"> {{fa>angle-double-down}}<button collapse="collvarAdvanced" type="link">Bereich ausklappen/einklappen </button> <collapse id="collvarAdvanced"> ==== 3.1 Call by value vs. call by reference ==== 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: <sxh python;title:Deklaration von drei Variablen> a = 10 b = 13.2 name1 = "Hans Meier" </sxh> |< 300px 150px 150px >| ^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? <sxh python;title:Variable an eine andere binden (Integer)> a = 3 b = a a = 5 # a ändern print(b) </sxh> Was denkst du wird am Schluss angezeigt für den Wert von b? Verwenden wir statt ganzer Zahlen eine Liste: <sxh python;title:Variable an andere binden (Liste)> list1 = [1,2,3] list2 = list1 list1.append(20) # die Zahl 20 zur Liste list1 hinzufügen print(list2) </sxh> Nun ändern wir den Wert der Variablen ''list1'' anders: <sxh python;title:Variable binden und ändern (2)> list1 = [1,2,3] list2 = list1 list1 = [100,101] print(list2) </sxh> <WRAP round todo 80%> **Aufgaben C** - 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. - Was bedeutet es, wenn eine Variable ** mutable ** bzw. ** immutable ** ist. Welche Variablentypen sind in Python **mutable** bzw. **immutable**? - "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. </WRAP> === 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: <sxh python;title:Pseudo-Beispiel für Call-by-value> my_number = 5 def add_one(number): number = number + 1 add_one(my_number) # der WERT von my_number wird übergeben print(my_number) </sxh> 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: <sxh python> list1 = [1,2,3] list2 = list1 </sxh> |< 450px 150px 150px 150px >| ^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): <sxh python;title:Pseudo-Beispiel für Call-by-Reference> 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. </sxh> Würde der Parameter über die Referenz übergeben, würde im obigen Beispiel die Variable ''my_number'' auf 6 geändert. ==== 3.3 Wie sieht es in Python aus? "Call by Object Reference" ==== 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 ** <sxh python> 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 </sxh> ** Wenn die Variable mutable ist, verhält sich Python wie Call by Reference ** <sxh python> 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] </sxh> ** Wenn ein neues Objekt mit einer neuen Adresse erstellt wird: ** <sxh python> 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 </sxh> <WRAP round todo 80%> **Aufgabe D** - Betrachte das untenstehende Programm und erkläre Zeile für Zeile, was genau geschieht. - Was wird am Schluss in l1 bzw. l2 stehen? <sxh python> 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) </sxh> </WRAP> </collapse> </callout> <callout type="warning" title="4. Listen (Arrays) erstellen, durchgehen und der range-Befehl"> {{fa>angle-double-down}}<button collapse="colllists" type="link">Bereich ausklappen/einklappen </button> <collapse id="colllists"> ==== 4.1 Listen erstellen ==== 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]'' <code Python> >>> Liste1 = [1, 2, 3, 4, 5] >>> Liste2 = [51, "Hallo", True, 3.141592] >>> Liste1[1] 2 >>> Liste2[-1] 3.141592 >>> </code> 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. <WRAP round todo 80%> **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. </WRAP> ==== 4.2 Der range-Befehl ==== 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] | <WRAP round todo 80%> **Aufgabe F** Erstelle mit dem ''range''-Befehl die folgenden Listen: - ''L1'' die Liste der ganzen Zahlen zwischen 0 und 8. - ''L2'' die Liste der ganzen Zahlen, die kleiner als 20 und durch 3 teilbar sind. - ''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)'' </WRAP> ==== 4.3 Listen durchgehen ==== 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: <sxh python;title:Das ursprüngliche Programm für die Spirale> 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) </sxh> Wir sagen Python einfach: Gehe alle Zahlen von 1 bis 6 durch (sage der Variablen z.B. ''zahl'') und mache dann die beiden Befehle: <sxh python> agent.move(FORWARD,zahl) agent.turn(richtung) </sxh> Damit sieht unser Programm viel kürzer aus: <sxh python> def baueSpirale(richtung): for zahl in [1,2,3,4,5,6]: agent.move(FORWARD,zahl) agent.turn(richtung) </sxh> Die Liste [1,2,3,4,5,6] können wir uns natürlich auch mit dem Range-Befehl erstellen lassen: <sxh python;title:Endgültige Version mit range> def baueSpirale(richtung): for zahl in range(1,7): agent.move(FORWARD,zahl) agent.turn(richtung) </sxh> <well> Will man z.B. etwas in Minecraft 10-Mal wiederholen, so kann man einfach den Befehl: ''for i in range(10)'' verwenden. <sxh python> for i in range(10): # i geht die Zahlen von 0 bis 9 durch agent.move(FORWARD,2) agent.place(BACK) </sxh> </well> ==== Weitere Listenbefehle ==== ^ 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. | <WRAP round todo 80%> **Aufgaben G** - Schreibe eine Funktion ''LinieDuo(n)'', welche eine Linie mit zwei selbst gewählten Materialien baut, wobei es jeweils abwechselt. - Schreibe eine Funktion ''LinieTre(n)'', welche eine Linie mit drei selbst gewählten Materialien baut, wobei es jeweils abwechselt mit allen drei Materialien. - 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. - Schreibe eine Funktion ''LinieMitHöhe(n,h)'', welche eine Linie mit Länge n und Höhe h baut. - Schreibe eine Funktion ''quadrat(n)'', welche den Agenten dazu bringt, ein nicht ausgefülltes Quadrat der Länge n zu bauen. - Schreibe eine Funktion ''quadratf(n)'' welche ein gefülltes Quadrat baut. - Schreibe eine Funktion ''tower(n,h)'' welche einen Turm mit quadratischer Grundfläche der Breite n und der Höhe h baut. </WRAP> </collapse> </callout> <callout type="primary" title="5. Verzweigungen mit ''if'' und Schleifen mit ''while'' "> {{fa>angle-double-down}}<button collapse="collif" type="link">Bereich ausklappen/einklappen </button> <collapse id="collif"> ==== Verzweigungen mit if ==== Mit der ''if'' Anweisung kann man Befehle nur dann ausführen lassen, wenn eine bestimmte Bedingung wahr (True) ist. <sxh pyhton;title:Einfache if-Answeisung> if (agent.get_item_count(1)>0): # wenn noch Material im Fach 1 liegt agent.set_slot(1) # Das aktuelle Fach auf 1 setzen </sxh> Man kann mit dem ''if''-Befehl auch mehrere Bedingungen prüfen (''elif'') und am Schluss etwas ausführen, wenn keine Bedingung zutrifft (''else''): <sxh python;title:if-Anweisung mit elif> 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. </sxh> ==== Schleifen mit "while" ==== 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): <sxh python;title:while-Schleife> 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() </sxh> </collapse> </callout> <WRAP round todo 80%> **Aufgaben E ** - Erstelle eine Funktion ''linie(n, material)'', welche eine Linie der Länge n mit dem übergebenen Material erstellt. - Erstelle eine Funktion ''linie2(n, material1, material2)'', welche eine Linie der Länge n mit abwechselnden Materialien ''material1'' und ''material2'' macht. - Erstelle eine Funktion ''rechteck(n,m,material)'' welche ein Rechteck der Länge n und Breite m baut mit dem Material ''material'' - 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'' - 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. - Schreibe eine Funktion ''kugel(n)'', welche eine Kugel baut mit "Radius" n, ohne dabei die Funktion shapes.sphere vom Code-Builder zu nutzen. - Schreibe eine Funktion ''house(...)'', welche ein Haus baut.! </WRAP> [[ef:start|Zurück zur Übersicht]] ef/grundlagen_python.txt Zuletzt geändert: 2024/09/03 15:15von lehmannr