Spielend programmieren lernen

Programmierkurse für Kinder, Jugendliche und Erwachsene

User Tools

Site Tools


de:blog:2016:0718_python

Title: Programmieren lernen mit Python und Roguelikes Date: 2016-07-18 23:41 Modified: 2016-07-18 23:41 Tags: teaching, coding, game, python Googleadsense: False Slug: 20160718_python Authors: Horst JENS Summary: Um ein kleines Computerspiel zu schreiben braucht man weder großartige Programmierkenntnisse noch teure 3D-Engines. Die Fähigkeit, buchstabengetreu abzuschreiben, etwas Fantasie und ein gewisses Interesse am Genre der Roguelikes-Spiele reicht durchaus aus, um einen Workshop oder eine (Doppel)Stunde zum Thema abzuhalten. Bei meiner Arbeit mit meinen eigenen Programmierkursen und bei Workshops in Schulen ist mir aufgefallen dass gerade jüngere Kinder nicht unbedingt mit bunten Farben, Musik, Soundeffekten und auch visuellen Code-Bausteinen zum Programmieren “gelockt” werden müssen… einen Buchstaben (traditionell das @-Zeichen) durch ein ein- oder gar zweidimensionales Spielfeld aus farblosen Satzzeichen zu bewegen bietet Aufregung genug…

Kurzlink: http://goo.gl/QI093P Hashtag: #spielendprogrammieren

Um ein kleines Computerspiel zu schreiben braucht man weder großartige Programmierkenntnisse noch teure 3D-Engines. Die Fähigkeit, buchstabengetreu abzuschreiben, etwas Fantasie und ein gewisses Interesse am Genre der Roguelikes-Spiele reicht durchaus aus, um einen Workshop oder eine (Doppel)Stunde zum Thema abzuhalten.

screenshot textrogue
Screenshot: Textrogue

Bei meiner Arbeit mit meinen eigenen Programmierkursen und bei Workshops in Schulen ist mir aufgefallen dass gerade jüngere Kinder nicht unbedingt mit bunten Farben, Musik, Soundeffekten und auch visuellen Code-Bausteinen zum Programmieren “gelockt” werden müssen… einen Buchstaben (traditionell das @-Zeichen) durch ein ein- oder gar zweidimensionales Spielfeld aus farblosen Satzzeichen zu bewegen bietet Aufregung genug…

Zielpublikum, Voraussetzungen

Dieser Artikel richtet sich an Lehrer und Workshopleiter die ihren Schülern eine Einführung in die Welt der “echten” Programmiersprachen, im speziellen Python, bieten wollen. Für eine ernsthafte Beschäftigung mit der Programmiersprache Python kann ich jedem Leser die exzellente Einführung “A byte of Python” von Swaroop C H empfehlen. Weitere Python-Tutorials sind auf der Python-Homepage aufgelistet, deutschsprachige Hilfe gibt es u.a. im Python-Forum.

Folgende Annahmen werden getroffen:

WorkshopteilnehmerInnen (Schüler) haben:

  • keine oder nur wenig Programmiervorkenntnisse
  • begrenzte Aufmerksamkeitsspanne (Praxis vor Theorie)
  • begrenzte Zeit im PC-Raum (ein bis zwei Stunden)
  • rudimentäre Tipp-Kenntnisse (können eckige Kammern, geschweifte Klammern, und das @-Zeichen tippen, Zeilen löschen)
  • pro Kursteilnehmer steht ein PC zur Verfügung mit:
  • Interesse an Computerspielen, speziell an Roguelikes
  • Experimentierfreude und Spieltrieb
  • 75 Codezeilen (Listing 1) sind von Kursteilnehmern im Rahmen des Workshops abtippbar bzw. diese sind schlau genug sie im Internet zu finden und per Copy and Paste einzufügen.

Hinweis: folgende Zeilennummern beziehen sich auf Listing 001

Roguelikes

Rogue (englisch für “Schurke, Räuber”) ist ein Computerspiel welches bereits im Computer-Mittelalter (1980) auf Uni-rechnern gespielt wurde. Da die damaligen Computer im Wesentlichen nur Text in einem Terminal darstellen konnte wurde genau dies zum Spielen verwendet: Man sieht den Spieler von oben durch ein Labyrinth (Dungeon) wandern, wobei er Schätze und Nahrung aufsammelt, Gegner bekämpft, Fallen ausweicht und Stiegen findet um noch tiefere, noch gefährlichere (Dungeons zu erforschen.

Moderne Computerspiele wie z.B. Diablo sind Nachfahren von Rogue und werden als Roguelikes oder “Dungeon Crawler” bezeichnet.

Python

Python (Version3.5) steht als freie Software für Linux, Mac und Windows unter http://python.org zum Download bereit steht. Am RaspberryPi und auf vielen Linux-Computern ist schon Python in Version2 vorinstalliert, ich empfehle aber Python ab Version 3 zu installieren. Das abgebildete Python-Beispielprogramm (Listing001) funktioniert notfalls auch mit Python Version2, sofern man in Listing001, Zeile 29 den Befehl input durch raw_input ersetzt. Falls das Installieren von Python im eigenen PC-Saal undurchführbar ist lässt sich das Beispiel auch online ausführen auf Webseiten, welche Python-im-Browser anbieten. Vorsicht, bei den Python-im-Browser Webseiten

Arbeitsablauf

Bei meinen eigenen Workshops handle ich stets nach der Devise “erst abtippen, Erklärungen nur nach Bedarf”. Die Minimalvariante besteht darin den abgebildeten Python-Code abzutippen und entweder auf einer Python-im-Browers Webseite oder in einem Code-Editor auszuführen und zu spielen.

Listing001. Klicken falls hier kein Python-Code steht

Code-Editor

Bei Python mitinstalliert wird der Editor IDLE, welcher im interaktiven Modus (Shell) startet. Dieser Modus ist nur für winzige (einzeilige) Programme gedacht, deshalb mittels File -> New File ein echtes Programmierfenster öffnen. Das Programm startet man mittels Run → Run Module. Davor wird man aufgefordert das Programm abzuspeichern, wobei die Dateiendung .py verwendet werden muss, z.B. textrogue.py. Im Internet findet man eine große Auswahl von Python-Editoren.

Spielanleitung

Der Spieler (dargestellt als **@*) wird mit den aus Egoshootern vertrauten Tasten w, a, s und d bewegt, wobei nach jeder Befehlseingabe dieEnter* - Taste gedrückt werden muss; help zeigt einen Hilfstext an der alle Befehle erklärt. Auch ohne Programmierkenntnisse ist es ab diesem Zeitpunkt möglich die Spielfigur zu ändern (Listing001, Zeile 9) und das Labyrinth (Zeile 3 bis 7) zu erweitern oder zu verändern. Zu beachten ist dabei dass die Umfassungsmauer (#) lückenlos den äußeren Rand bilden muss.

Dies ist ein guter Zeitpunkt um die Bedeutung der “Einfg” - Taste (Ins-Key) zu erklären, welche in den meisten Code-Editoren zwischen dem Einfügen- und dem Überschreiben-Modus wechselt.

Für weitergehende Änderungswünsche sind minimale Programmierkenntnisse oder eine gute Portion ausprobieren erforderlich. Ich gehe im weiteren Artikel davon aus dass solche beim Workshopleiter/Lehrer vorhanden sind, z.B. durch Beschäftigung mit visuellen Programmiersprachen wie z.B. Scratch oder PocketCode oder ähnlichen Projekten.

Python-Besonderheiten

Wer erstmals mit Python arbeitet muss aufpassen: Nicht nur ist die Sprache case sensitive (Groß- und Kleinschreibung spielt eine Rolle), es ist auch sehr wichtig wie weit eine Zeile links eingerückt ist und ob dazu Leerzeichen oder die Tabulatortaste verwendet wurde. Prinzipiell signalisiert eine Einrückung (die nur nach einer Zeile welche mit einem Doppelpunkt endet erfolgt) einen Programmblock. Zum Beispiel einen Block der nur nach einer bestimmten Bedingung ausgeführt wird oder welcher öfter wiederholt wird. In anderen Sprachen werden diese Blöcke oft durch geschweifte Klammern eingeschlossen, bei visuellen Programmiersprachen wie z.B. Scratch werden die Blöcke von anderen, Schraubstock-förmigen Blöcken umrahmt.

Um überlange Zeilen umzubrechen gibt es die Möglichkeit, die Zeile mit einem \ zu beenden und in der nächsten Zeile (mit beliebiger Einrückung) fortzusetzten. Lange Zeilen darf man auch aufteilen sofern man sich innerhalb von Klammern befindet: In Listing 001 bilden z.B. die Zeilen 63, 64 und 65 eine einzige Codezeile. Strings (Zeichenketten) können durch einfache (‘) oder doppelte (“) Anführungszeichen gekennzeichnet werden. Werden drei Anführungszeichen verwendet (Triple-Quotes), so darf der String mehrere Zeilen lang sein (Zeile 2 bis 8). Sofern eine Raute nicht innerhalb von Anführungszeichen steht ist alles rechts von der Raute ein Kommentar. Kommentare sind nur für Menschen wichtig, nicht für Python und können weggelassen werden, genauso wie Leerzeilen.

Schleifen, Verzweigung, Variablen und erste Änderungen

Wie in jeder prozeduralen Programmiersprache gibt es auch in Python Schleifen (for, while, break, continue), Verzweigungen (if, elif, else) und Variablen. Die Zuweisung eines Wertes zu einer Variablen erfolgt durch das = Zeichen , wobei es möglich ist gleich mehreren Variablen in einer einzigen Zeile Werte zuzuweisen (Zeile 16):

hunger, treasure, food = 0, 0, 7

Obige Zeile bedeutet dass die Variable hunger den Startwert 0, bekommt, ebenso die Variable treasure, wohingegen die Variable food den Wert 7 bekommt (damit der Spieler nicht sofort verhungert).

In Zeile 18 fängt die while Schleife an. Der eingerückte Block unter der while-Schleife (Zeile 19 bis 74) wird ausgeführt so lange der Ausdruck (expression) rechts vom Wort while wahr ist. Der Ausdruck ist eine Frage die vom Computer mit logisch wahr (True) oder falsch (False) beantwortet werden kann. Üblicherweise ist diese Frage ein Vergleich, z.B. hitpoints < 1 oder hunger > 100. Vergleichsoperatoren (<, <=, ==, !=, >=, >), Logik-Operatoren (or, and, not) und Variablen werden of zusammen in einem Ausdruck verwendet.

Audrücke kommen auch nach jedem if und elif (eigentlich: else if) vor.

Zeit für Veränderungen. Wer in der while-Schleife den erlaubten maximalwert für hunger deutlich senkt, den Wert für food in Zeile 16 auf 0 setzt und die Nahrungspakete (f) im Dungeon (Zeilen 3 bis 7) strategisch knapp verteilt verwandelt das Roguelike in ein Denkspiel.

Wie viel Hunger die Spielfigur aushält steht in Zeile 18, wie sehr ein Nahrungspaket sättigt steht in Zeile 41.

Textausgabe verschönern

Für die Textausgabe im Program Listing001 ist die ''%%print%%''-Funktion zuständig, welche Text am Bildschirm ausgibt. Dabei wird Gebrauch von Pythons Format-Specification-Mini-Language gemacht, wie hier im Listing 001, Zeile 27 und 28, hier zusammengefasst in eine Zeile:

status = 'hunger:{} food:{} gold:{}\n'.format(hunger, food, treasure)

Das \n bedeutet einen Zeilenwechsel (line feed + carriage return). Die Werte der Variablen hunger, food und treasure werden in dieser Reihenfolge in die Platzhalter (geschweifte Klammern) eingfefügt.

Das gleiche Aussehen würde übrigens dieser Befehl hervorrufen, str() wandelt dabei eine Zahl in einen String um:

status = 'hunger:" + str(hunger) + " food:" + str(food) + " gold:" + str(treasure)" + "\n"

Mit diesem Wissen ausgestattet lässt sich jede print-Ausgabe im Spiel verschönern, bzw. die Werte der variablen message verbessern. Wer das Symbol für den Spieler ändern will wird in Zeile 9 fündig.

Weitere Änderungsvorschläge: Mehr Gegenstände

Gewünscht wird von meinen Kursteilnehmern oft eine Erweiterung des (Bei)spiels um Fallen und Monster. Nichts einfacher als das, zumindest solange die Monster (vorerst) stationär sind: In den Dungeon-String (zeile 3-7) werden ein paar Statuen eingebaut (S). Läuft der Spieler in diese hinein zersplittern diese und der Spieler verliert Lebenspunkte (hitpoints). Dazu muss der Spieler erst einmal hitpoints haben.

Wichtig: Im folgenden bitte immer die Original-Einrückung unverändert lassen! Die folgende Zeilenangaben beziehen sich auf Listing001.

Wir setzen in der leeren Zeile 17 den Wert für die Variable player_hitpoints auf 25:

player_hitpoints = 25

Die hitpoints müssen auch angezeigt werden, in den Zeilen 27 und 28 wird daher die Variable status verändert:

status = 'hitpoints: {} hunger:{} food:{} gold:{}\n'.format(
          player_hitpoints, hunger, food, treasure)

Und schlussendlich brauchen wir eine Kollisionserkennung. Diese gibt es schon, wir ergänzen Zeile 61:

elif target in ['f', '$', "S"]:    # run into something interesting

Jetzt müssen wir nur noch den Hiptointverlust einbauen. Dazu den Textcursor auf das Ende der Zeile 71 setzen und mehrmals die Enter-Taste drücken. Alle folgenden Zeilen wandern nach unten. In die jetzt leere Zeile 72 schreiben wir mit der selben Einrückung wie Zeile 69:

elif target == "S":
    message = "you destroy a statue and loose hitpoints"
    player_hitpoints -= 5

Die beiden elif Befehle in Zeile 69 und in Zeile 72 müssen exakt untereinander stehen.

Schlussendlich soll das Spiel aufhören wenn der Spieler zu wenig hitpoints hat. Wir verändern Zeile 18:

while hunger < 100 and player_hitpoints > 0:

Fertig! (siehe auch Listing 2)

Nach diesem Schema können weitere (aufsammelbare bzw. zerstörbare) Gegenstände in das Spiel eingefügt werden:

  • Zeichen für neuen Gegenstand ausdenken und im dungeon einbauen ( Zeile 3 bis 7)
  • Zeichen für neuen Gegenstand in Kollisionserkennung einbauen (Zeile 61)
  • Einen neuen elif-Block schreiben wie in Zeile 72-74 und unter diesen einfügen. Dort steht was das Aufsammeln des neuen Gegenstandes bewirkt.
  • Wenn eine neue Variable notwendig ist (z.B. Magiepunkte oder Heiltränke):
  • Variable deklarieren und Startwert festlegen, noch vor Beginn der while-Schleife. z.B. in Zeile 12
  • Die neue Variable im Status anzeigen (Zeile 27 + 28)
  • Wenn der Spieler durch die neue Variable sterben kann: Schleifenbedingung in Zeile 18 anpassen

Beispiele für weitere Gegenstände sind Heiltränke, besseres Essen, Schriftrollen, Edelsteine, Waffen etc.

Listing 002 (Statuen und Hitpoints). Klicken falls hier kein Python-Code steht

Noch mehr Änderungsvorschläge: hüpfen

Alle folgenden Zeilennummern beziehen sich auf Listing002.

Die Bewegung der Spielfigur wird in den elif-Blöcken (Zeile 47-55) geregelt, durch Veränderung der Variablen delta_x und delta_y. Will man die Spielfigur 2 Felder weit springen lassen, bietet es sich an z.B. die Großbuchstaben W,A,S,D zu verwenden und den movement-keys elif-Block (Zeile 48-55) zu kopieren und gleich darunter verändert einzufügen:

elif command == "A":
    delta_x = -2  # jump left
elif command == "D": 
    delta_x = 2   # jump right 
elif command == "W": 
    delta_y = -2  # jump up
elif command == "S":
    delta_y = 2   # jump down

Soll das hüpfen hungriger machen als normales gehen ist nach jeder elif-Zeile folgendes (eingerückt!) einzufügen:

    hunger+=1

Außerdem sollte dann der Hilfstext in Zeile 11 angepasst werden.

HELPTEXT = 'movement: w,a,s,d\njump: W,A,S,D\neat: e\nquit: quit or q' 

Um Hinaushüfen aus dem Dungeon zu vermeiden müssen wir wissen wie breit der Dungeon ist. Dazu gibt es in Python die eingebaute Funktion ''%%len()%%''. Unter die Zeile 15 (lines = DUNGEON.split()) schreiben wir:

length = len(lines[0])

und speichern damit die Breite der ersten Zeile im Dungeon in die Variable length.

Jetzt müssen wir noch prüfen ob sich das target im erlaubten Dungeon Bereich befindet. Zwischen den Zeilen # ----- check if movement is valid ------ und target = lines[player_y + delta_y][player_x + delta_x] verschaffen wir und mittels der Enter-Taste ein wenig Platz und fügen folgenden Block ein:

if (player_y + delta_y < 0 or player_y + delta_y >= len(lines) or 
       player_x + delta_x < 0 or player_x + delta_x >= length):
       message = "illegal move" 
       continue # back to the start of the while loop

Alternativ dazu könnte man einen Error-Handler mit ''%%try/catch%%'' schreiben.

Meiner Erfahrung nach bevorzugen Schüler die praxisnähere Lösung, stattdessen die Begrenzungsmauer im Dungeon (#) doppelt so dick zu zeichnen. Und, wenn sie schon dabei sind, die Gelegenheit zu nutzen um den Dungeon ordentlich zu vergrößern.

Änderungsvorschläge: Zufallszahlen und Kämpfe

Alle folgende Zeilenangaben beziehen sich auf das durch obigen Code veränderte Listing002. Sicherheitshalber sind die Orignalzeilen zitiert

Zufallszahlen bringen noch mehr Spannung ins Spiel. Voraussetzung ist dafür das importieren des moduls random in der ersten Zeile. Alle anderen Zeilen wandern dadurch nach unten:

import random

Dank des Random - Moduls können sehr einfach ganzzahlige Zufallszahlen erzeugt werden. Will man z.B. den Nahrungswert von Lebensmittelpaketen (wie sehr Hunger durch essen reduziert wird) anstatt auf den fixen Wert 11 auf einen Zufallswert zwischen 5 und 15 verändern, so ersetzt man Zeile 43 (hunger -= 11) durch:

hunger -= random.randint(5,15)

Um die Kämpfe gegen die Statuen spannender zu machen empfiehlt sich eine Kombination aus Wahrscheinlichkeitsrechnung und Zufallszahlen für den Schaden. Der bisherige Code für Statuen schaut so aus, eine Statue wird immer zerstört und der Spieler verliert 5 player-hitpoints (Listing002, Zeile 90 bis 92):

elif target == "S":
   message = "you destroy a statue and loose hitpoints"
   player_hitpoints -= 5

Das Random modul bietet auch eine Funktion namens random.random() welche Zufalls-Dezimalzahlen zwischen 0 und 1 errechnet und sich ideal für Wahrscheinlichkeiten anbietet.

Nehmen wir an die Statue schlägt in 70% (eine andere Schreibweise dafür ist: 0.7) aller Fälle zurück (ohne zerstört zu werden) und der Spieler verliert dabei jeweils 1 bis 6 hitpoints. Der obige Code-Block wird gelöscht. Folgender Code-Block wird direkt unter der Kollisionsabfrage elif target in ['f', '$', 'S']: und noch vor die Zeile # replace target with floor tile eingefügt:

if target == "S":
    if random.random() < 0.7 : 
        damage = random.randint(1,6)
        message = "the statue fights back, you loose {} hitpoints".format(damage)
        player_hitpoints -= damage
        continue    # back to the start of the while loop
    message =  "you destroy the statue!"   

Bei derart kampfstarken Statuen empfiehlt es sich die Anfangshitpoints des Spielers (Variable player_hitpoints) stark zu erhöhen bzw. Heiltränke im Dungeon zu verteilen.

Abschließend sorgen wir noch für eine aktualisierte Statusanzeige, welche nur angezeigt wird wenn die While-Schleife nicht durch ein break verlassen wurde sondern weil die Schleifenbedingung False wurde. In der vorletzten Zeile fügen wir ein:

else:
    print("hitpoints: {}, hunger: {}".format(player_hitpoints, hunger))

Den Kompletten Programmcode mit kämpfenden Statuen gibt es in hier:

Listing003 (klicken wenn hier kein Python-Code angezeigt wird)

Um bewegliche Monster zu bauen, ist eine Beschäftigung mit Klassen (objektorientierte Programmierung) hilfreich. Anleitung erfolgt möglicherweise in einem nachfolgenden Blogposting.

Erklärungungen für wirklich Neugierige

Meiner Unterrichtserfahrung nach ist der Bedarf nach Erklärungen in einem zeitlich begrenzten Workshop eher gering. Die Schüler welche sich wirklich interessieren fragen danach, ihre Kollegen sind eher damit beschäftigt Änderungen auszuprobieren, Fehler zu suchen und das Spiel zu testen. Im Gegensatz dazu tendieren Erwachsene dazu alles sofort verstehen zu wollen. Anbei Erklärungen die eher für die Workshopleitung oder interessierte SchülerInnen gedacht sind:

Spielfigur und Spielbrett

Alle folgenden Zeilennummern, sofern nicht anders angegeben, beziehen sich auf Listing001

Das Labyrinth oder Spielbrett wird in der Variable dungeon (Zeile 2) gespeichert, dessen Wert ist ein mehrzeiliger String (Zeichenkette), erkennbar an den dreifachen Anführungszeichen (Triple-Quotes) am Anfang und am Ende. In Python ist jeder Buchstabe in einem String indiziert, dass heißt er hat eine Nummer. Die Zählung beginnt verwirrenderweise bei 0. Angenommen ich habe einen ein-dimensionalen Dungeon (einen langen Gang), bestehend aus drei Bodenplatten (Punkten), einem Nahrungspaket (f), einem Schatz ($) und einer Mauer (#):

dungeon = "...f$#"
# index:   012345
# nummer:  123456

Das erste Zeichen im String (die erste Bodenplatte) hat den Index 0, das Nahrungspaket (das 4. Zeichen) hat den Index 3, der Schatz hat den Index 4 und die Mauer den Index 5.

Möchte ich in diesen Dungeon einen Spieler (@) zeichnen der auf der dritten Bodenplatte (links vom Nahrungspaket) steht, überdeckt der Spieler auf Index 2 die Bodenplatte. Da sich der Spieler später weiterbewegt verändere ich nicht direkt den Wert der Variable dungeon sondern sage Python, es soll den dungeon-Teil links vom Spieler (die ersten beiden Bodenplatten, Index 0 bis 1) darstellen, dann den Spieler selbst (auf Index 2) und danach den Dungeon-Teil rechts vom Spieler (Index 3 bis 5).

Die Positon vom Spieler speichere ich in der Variabele px (x-position of player) und gebe ihr den Wert 2. Der Python-Code würde so ausschauen:

dungeon = "...f$#"
px = 2
print(dungeon[:px]+"@"+dungeon[px+1:])

Das Ergebnis:

..@f$#

Python kann einen Teilstring anzeigen indem man nach dem Namen des Strings in eckigen Klammern den Index schreibt (sofern man nur ein einziges Zeichen schreiben will) oder in die eckigen Klammern den Startindex, einen Doppelpunkt und den Stopp-Index schreibt. Der Stopp-Index ist dabei jenes Zeichen das nicht mehr gedruckt werden soll. Wird die Zahl links vom Doppelpunkt weggelassen bedeutet das “vom ersten Zeichen an”, wird die Zahl rechts vom Doppelpunkt weggelassen bedeutet das “bis zum letzten Zeichen”. Der print-Befehl sorgt für die Ausgabe am Bildschirm. Obiges Beispiel bedeutet: “Drucke die Zeichenkette welche in der Variablen dungeon gespeichert ist vom ersten (Index 0) Zeichen an bis zum zweiten Zeichen (Index 1), dann drucke einen Klammeraffen (@), dann drucke vom 4.Zeichen (2+1=3) bis inklusive zum letzten Zeichen. Im Beispiel-Programm Listing001 passiert genau das in Zeile 23, allerdings nur wenn in der gerade dargestellten Zeile (mit Index y) sich auch der Spieler befindet (Variable player_y). Ansonsten wird die dungeon-Zeile so gedruckt wie sie ist, ohne verdeckende Spielfigur (Zeile 25).

Sammelt die Spielfigur Gegenstände (Nahrung, Schätze) aus dem Labyrinth auf, muss der Dungeon dauerhaft verändert werden.

Zur Erklärung: der Mehrzeilige dungeon wird mit ''%%split()%%'' in eine Liste einzelner Zeilen zerlegt, welche in der Variablen lines gespeichert werden (Zeile 15).

Diese Zeilen haben ebenfalls einen Index, wiederum beginnend bei 0. lines[0] ist die erste Zeile, lines[1] die zweite Zeile usw.

Um zum Beispiel ein aufgesammeltes Nahrungspaket durch eine Bodenplatte zu ersetzten, muss die entsprechende Linie (line) in der Liste lines verändert werden.

Dies geschieht indem man ein 2-dimensionales Array anspricht, mit 2 eckigen Klammerpaaren hintereinander.

Die erste Dimension bildet die Linie in der verändert werden soll (y). Die zweite Dimension ist das Zeichen innerhalb dieser Linie (x).

Dies geschieht in dem Augenblick im Programm bevor der Spieler die neue Position erreicht hat (er könnte ja auch in eine Mauer laufen). Seine Laufrichtung wird in den Variablen delta_x und delta_y gespeichert. Seine zukünftige Position ergibt sich aus player-x, wo er gerade steht) und delta_x (Laufrichtung) bzw. player_y + delta_y.

Zuerst wird die Linie gesucht in der sich der Spieler befinden wird:

lines[player_y+delta_y]

Diese Linie ist ein großer Textstring. Sie wird zerlegt in den Teil links vom Spieler [:player_x+delta_x] und in den Teil rechts vom Spieler [player_x+delta_x+1:]. Dazwischen kommt das Bodenplattenzeichen des Dungeons, ein Punkt. Die komplette Zeile (63 bis 65, hier zusammengefasst) lautet:

lines[player_y + delta_y] = (lines[player_y + delta_y][:player_x + delta_x] + '.' + lines[player_y + delta_y][player_x + delta_x + 1:])

Was bedeutet das enumerate in Zeile 20

for y, line in enumerate(lines):

Dies ist ein Trick der es erspart eine Variable y zu definieren, auf 0 zu setzten und nach jeder Linie von lines um eins zu erhöhen. enumerate(lines) numeriert die Linien (natürlich pythonesk mit 0 beginnend) für uns durch. Die for schleife “schleift” über die Variable y und die dazugehörende line von lines gleichermaßen.

Ohne enumerate würde der Code 2 Zeilen länger werden und so ausschauen ab Zeile 20:

for line in lines:
    y=0 # schleifenvariable, index der Zeile
    if y == py:
        print(line[0:px]+player+line[px+1:])
    else:
        print(line)
    y+=1

Der Unterschied zwischen break und continue

Eine while-Schleife kann man in Python auf 2 Arten verlassen:

  • indem die Schleifenbedingung falsch wird ( z.B. zu viel Hunger, zuwenig Hitpoints)
  • indem man auf ein break Kommando stößt. Dies passiert in Zeile 30 (Listing 1) wenn der Spieler als Kommando quit eingibt.

Im Gegensatz zu break dient der continue-Befehl dazu sofort an den Schleifenanfang zu springen und den restlichen Codeblock innerhalb der Schleife zu ignorieren. Dies wird in Listing003 bei den kämpfenden Statuen verwendet: der Spieler ist in eine Statue hineingelaufen, diese steht aber immer noch da. Anstatt neue Werte für delta_x und delta_y zu berechnen springt python einfach zurück an den Beginn der while-Schleife und ignoriert dadurch die letzten Zeilen innerhalb der Schleife welche die position player_x und player_y auf die selbe Position wie die noch vorhandene Statue setzten würden.

Warum werden die Namen von manchen Variablen in Großbuchstaben geschrieben

Es gibt einen eigenen Python Style Guide (Pep8) welcher vorschreibt wie man z.B. Variablen so benennt dass andere Programmierer den Code leichter lesen können. Man muß sich zwar nicht an den Style-Guide (Python funktioniert trotzdem), es wird aber sehr empfohlen. Im Style Guide steht zum Beispiel dass der Name von Konstanten (Variablen welche sich im Programm nicht mehr ändern) in Großbuchstaben geschreiben werden soll.

Dieser Artikel steht unter einer Creative-Commons Share Alike 4.0 Lizenz.

/var/www/horst/spielend-programmieren.at/data/pages/de/blog/2016/0718_python.txt · Last modified: 2017/10/12 09:40 (external edit)