Zum Inhalt springen

cat posts/python-lernen-04-sammlungen.md

Python lernen (Teil 4): Sammlungen

Listen, Tupel, Dictionaries und Sets – damit beschreiben wir die Dungeon-Welt als Daten und geben unserem Abenteurer ein richtiges Inventar.

In Teil 3 haben wir einen Game-Loop gebaut, der auf die Befehle des Spielers reagiert. Die möglichen Wege und Ereignisse steckten dabei direkt in einer langen if-elif-Kette.

Bei zwei oder drei Räumen funktioniert das noch. Besteht unser Dungeon später aber aus dreißig Räumen, würde der Game-Loop schnell unübersichtlich werden.

Heute lernen wir deshalb Sammlungen kennen. Damit können wir mehrere Werte gemeinsam speichern und die Welt unseres Dungeons als Daten beschreiben.

Python bringt dafür verschiedene Collection Types mit:

  • Listen
  • Tupel
  • Dictionaries
  • Sets

Sie erfüllen unterschiedliche Aufgaben. Am Ende dieses Teils verwenden wir sie, um Räume, Ausgänge, Gegenstände und den Player State übersichtlich abzubilden.

Listen: geordnet und veränderbar

Eine Liste speichert mehrere Werte in einer festen Reihenfolge. Listen werden mit eckigen Klammern geschrieben:

inventar = ["Fackel", "Brot", "Seil"]

Die einzelnen Werte heißen Elemente. Eine Liste darf beliebig viele Elemente enthalten und kann auch leer sein:

leeres_inventar = []

Auf Elemente zugreifen

Jedes Element besitzt eine Position, die als Index bezeichnet wird. Die Zählung beginnt bei 0:

inventar = ["Fackel", "Brot", "Seil"]

print(inventar[0])
print(inventar[1])
print(inventar[2])

Die Ausgabe lautet:

Fackel
Brot
Seil

Die Indizes dieser Liste sehen damit so aus:

Index Element
0 "Fackel"
1 "Brot"
2 "Seil"

Dass die Zählung bei 0 beginnt, ist am Anfang ungewohnt, in der Programmierung aber sehr verbreitet.

Mit einem negativen Index greifst Du vom Ende der Liste aus auf ein Element zu:

print(inventar[-1])

Die Ausgabe lautet:

Seil

-1 bezeichnet das letzte Element, -2 das vorletzte und so weiter.

Listen verändern

Listen sind mutable, also veränderbar. Mit append(...) fügst Du ein einzelnes Element am Ende hinzu:

inventar = ["Fackel", "Brot"]
inventar.append("Schlüssel")

print(inventar)

Die Ausgabe lautet:

['Fackel', 'Brot', 'Schlüssel']

Mit remove(...) entfernst Du ein bestimmtes Element:

inventar.remove("Brot")

print(inventar)

Danach enthält die Liste nur noch:

['Fackel', 'Schlüssel']

Soll eine Liste um alle Elemente einer anderen Liste erweitert werden, verwendest Du extend(...):

inventar = ["Fackel"]
fund = ["Schlüssel", "Heiltrank"]

inventar.extend(fund)

print(inventar)

Die Ausgabe lautet:

['Fackel', 'Schlüssel', 'Heiltrank']

Der Unterschied ist wichtig:

inventar.append(fund)

würde die gesamte Liste fund als ein einziges Element einfügen:

['Fackel', ['Schlüssel', 'Heiltrank']]

Anzahl der Elemente bestimmen

Mit len(...) ermittelst Du die Länge einer Liste:

inventar = ["Fackel", "Brot", "Seil"]

print(len(inventar))

Die Ausgabe lautet:

3

Eine leere Liste besitzt die Länge 0.

Prüfen, ob ein Element enthalten ist

Mit in kannst Du prüfen, ob sich ein Wert in einer Liste befindet:

inventar = ["Fackel", "Brot", "Seil"]

print("Brot" in inventar)
print("Schlüssel" in inventar)

Die Ausgabe lautet:

True
False

Das lässt sich direkt in einer Bedingung verwenden:

if "Schlüssel" in inventar:
    print("Du kannst die Tür öffnen.")

Eine Liste durchlaufen

Mit einer for-Schleife kannst Du jedes Element nacheinander verarbeiten:

inventar = ["Fackel", "Brot", "Seil"]

for gegenstand in inventar:
    print(f"- {gegenstand}")

Die Ausgabe lautet:

- Fackel
- Brot
- Seil

Du benötigst dafür keinen Index. Bei jedem Durchlauf enthält gegenstand automatisch das nächste Element der Liste.

Tupel: geordnet und unveränderbar

Ein Tupel ähnelt einer Liste. Es ist ebenfalls geordnet und kann mehrere Werte enthalten. Ein Tupel kann nach seiner Erstellung jedoch nicht mehr verändert werden.

Tupel werden üblicherweise mit runden Klammern geschrieben:

position = (3, 7)

Auf die Elemente greifst Du wie bei einer Liste über ihren Index zu:

print(position[0])
print(position[1])

Die Ausgabe lautet:

3
7

Eine Position aus einer X- und einer Y-Koordinate ist ein typischer Anwendungsfall für ein Tupel: Beide Werte gehören zusammen und die Position soll als gemeinsame Einheit behandelt werden.

Du kannst die Werte auch direkt in zwei Variablen übernehmen:

position = (3, 7)
x, y = position

print(f"X: {x}, Y: {y}")

Das wird Unpacking genannt.

Tupel lassen sich nicht verändern

Der folgende Versuch führt zu einem TypeError:

position = (3, 7)
position[0] = 9

Python erlaubt es nicht, einem Element des Tupels nachträglich einen neuen Wert zuzuweisen.

Ein Tupel eignet sich deshalb für Werte, die zusammengehören und nicht versehentlich verändert werden sollen.

Ein Tupel mit nur einem Element

Bei einem Tupel mit einem einzigen Element ist das Komma entscheidend:

ein_element = ("Schlüssel",)

Ohne das Komma entsteht lediglich ein String in Klammern:

kein_tupel = ("Schlüssel")

print(type(kein_tupel))

Die Ausgabe lautet:

<class 'str'>

Technisch erzeugt also das Komma das Tupel, nicht allein die runden Klammern.

Dictionaries: Werte über Schlüssel erreichen

Ein Dictionary, kurz dict, ordnet Schlüsseln bestimmte Werte zu.

Während Du bei einer Liste über einen numerischen Index auf ein Element zugreifst, verwendest Du bei einem Dictionary einen selbst gewählten Schlüssel.

Ein einfacher Raum könnte so beschrieben werden:

raum = {
    "name": "Halle",
    "beschreibung": "Eine dunkle Halle mit moderigem Geruch.",
    "besucht": False,
}

Ein Dictionary wird mit geschweiften Klammern geschrieben. Jeder Eintrag besteht aus einem Schlüssel und einem Wert:

"beschreibung": "Eine dunkle Halle mit moderigem Geruch."

Der Doppelpunkt trennt den Schlüssel vom zugehörigen Wert.

Auf einen Wert zugreifen

Den Wert eines bestimmten Schlüssels erhältst Du mit eckigen Klammern:

print(raum["name"])
print(raum["beschreibung"])

Die Ausgabe lautet:

Halle
Eine dunkle Halle mit moderigem Geruch.

Werte verändern und ergänzen

Dictionaries sind wie Listen mutable. Du kannst vorhandene Werte verändern:

raum["besucht"] = True

Mit derselben Schreibweise kannst Du einen neuen Eintrag ergänzen:

raum["licht"] = "dunkel"

Existiert der Schlüssel bereits, wird sein Wert ersetzt. Existiert er noch nicht, wird er neu angelegt.

Prüfen, ob ein Schlüssel existiert

Der Operator in prüft bei einem Dictionary standardmäßig dessen Schlüssel:

print("beschreibung" in raum)
print("monster" in raum)

Die Ausgabe lautet:

True
False

Fehlende Schlüssel und .get(...)

Greifst Du auf einen nicht vorhandenen Schlüssel zu, entsteht ein KeyError:

print(raum["monster"])

Mit .get(...) erhältst Du bei einem fehlenden Schlüssel stattdessen None:

monster = raum.get("monster")

print(monster)

Du kannst außerdem einen Default-Wert angeben:

monster = raum.get("monster", "Kein Monster")

print(monster)

Die Ausgabe lautet:

Kein Monster

Dictionaries durchlaufen

Eine for-Schleife über ein Dictionary liefert standardmäßig seine Schlüssel:

for schluessel in raum:
    print(schluessel)

Mit .values() erhältst Du die Werte:

for wert in raum.values():
    print(wert)

Mit .items() erhältst Du Schlüssel und Wert gemeinsam:

for schluessel, wert in raum.items():
    print(f"{schluessel}: {wert}")

Das Unpacking funktioniert hier genauso wie bei einem Tupel.

Verschachtelte Collections

Die Werte eines Dictionaries dürfen selbst wieder Listen, Tupel, Sets oder weitere Dictionaries sein:

raum = {
    "name": "Halle",
    "ausgaenge": {
        "norden": "bibliothek",
        "osten": "krypta",
    },
    "gegenstaende": ["Fackel", "Brot"],
}

raum["ausgaenge"] liefert hier ein weiteres Dictionary:

print(raum["ausgaenge"])

Um das Ziel des nördlichen Ausgangs zu erhalten, werden zwei Zugriffe miteinander kombiniert:

print(raum["ausgaenge"]["norden"])

Die Ausgabe lautet:

bibliothek

Genau diese Verschachtelung nutzen wir gleich, um die gesamte Dungeon-Welt zu beschreiben.

Sets: ungeordnet und ohne Duplikate

Ein Set ist eine Sammlung, in der jeder Wert höchstens einmal vorkommt.

besuchte_raeume = {"halle", "bibliothek", "halle"}

print(besuchte_raeume)

Obwohl "halle" zweimal angegeben wurde, ist der Wert im Set nur einmal enthalten:

{'halle', 'bibliothek'}

Die Reihenfolge der Ausgabe kann variieren. Sets sind nicht dafür gedacht, Elemente über eine feste Position oder einen Index abzurufen.

Werte hinzufügen

Mit .add(...) fügst Du einem Set einen Wert hinzu:

besuchte_raeume.add("krypta")

Wird derselbe Wert erneut hinzugefügt, verändert sich das Set nicht:

besuchte_raeume.add("krypta")

"krypta" bleibt trotzdem nur einmal enthalten.

Mitgliedschaft prüfen

Auch bei Sets verwendest Du in:

if "krypta" in besuchte_raeume:
    print("Du warst bereits in der Krypta.")

Solche Prüfungen sind bei Sets besonders effizient. Sie eignen sich deshalb gut für Dinge wie:

  • bereits besuchte Räume
  • freigeschaltete Fähigkeiten
  • bekannte Zaubersprüche
  • einmalige IDs

Ein leeres Set anlegen

Ein leeres Set wird mit set() erzeugt:

besuchte_raeume = set()

Leere geschweifte Klammern erzeugen dagegen ein Dictionary:

leere_sammlung = {}

print(type(leere_sammlung))

Die Ausgabe lautet:

<class 'dict'>

Welche Collection passt wozu?

Collection Geordnet Veränderbar Duplikate Zugriff
Liste ja ja ja über Index
Tupel ja nein ja über Index
Dictionary ja ja Schlüssel eindeutig über Schlüssel
Set keine garantierte Reihenfolge ja nein über Mitgliedschaft

Für unseren Dungeon verwenden wir:

  • ein Dictionary für den Player State
  • eine Liste für das Inventar
  • ein Dictionary für die Räume
  • ein Dictionary für die Ausgänge jedes Raums
  • eine Liste für die Gegenstände in einem Raum
  • ein Set für bereits besuchte Räume

Die Dungeon-Welt als Daten

Bisher steckte die Welt direkt in der Programmlogik:

if befehl == "norden":
    print("Du stehst vor einer verschlossenen Tür.")
elif befehl == "osten":
    print("Im Osten wartet ein Goblin.")

Jeder neue Raum erforderte dadurch weitere Bedingungen.

Stattdessen beschreiben wir die Welt nun in einem großen Dictionary:

raeume = {
    "halle": {
        "name": "Halle",
        "beschreibung": "Du stehst in einer dunklen Halle mit moderigem Geruch.",
        "ausgaenge": {
            "norden": "bibliothek",
            "osten": "krypta",
        },
        "gegenstaende": ["Fackel"],
    },
    "bibliothek": {
        "name": "Bibliothek",
        "beschreibung": "Staubige Regale voller zerfledderter Bücher umgeben Dich.",
        "ausgaenge": {
            "sueden": "halle",
        },
        "gegenstaende": ["Schlüssel"],
    },
    "krypta": {
        "name": "Krypta",
        "beschreibung": "Feuchtigkeit glänzt auf den Wänden der alten Krypta.",
        "ausgaenge": {
            "westen": "halle",
        },
        "gegenstaende": [],
    },
}

Die Schlüssel "halle", "bibliothek" und "krypta" sind interne IDs der Räume. Jeder Raum ist wiederum ein Dictionary mit vier Einträgen:

  • name
  • beschreibung
  • ausgaenge
  • gegenstaende

Die Ausgänge bilden Richtungen auf die ID des jeweiligen Zielraums ab:

"ausgaenge": {
    "norden": "bibliothek",
    "osten": "krypta",
}

Dadurch müssen wir nicht mehr für jede Richtung einen eigenen elif-Block schreiben.

Den Player State gruppieren

Auch der Player State besteht inzwischen aus mehreren zusammengehörigen Werten:

name = "Karl"
hp = 100
max_hp = 100
gold = 50
inventar = []

Diese Werte können wir ebenfalls in einem Dictionary gruppieren:

spieler = {
    "name": "Karl",
    "hp": 100,
    "max_hp": 100,
    "gold": 50,
    "inventar": [],
}

Der Zugriff erfolgt nun über die jeweiligen Schlüssel:

print(spieler["name"])
print(spieler["hp"])
print(spieler["inventar"])

So ist sofort erkennbar, dass diese Werte zum Player State gehören.

Der Dungeon, Schritt 4

Öffne dungeon.py und ersetze den bisherigen Inhalt durch folgenden Code:

# Dungeon – Teil 4: Collections und Welt als Daten
raeume = {
    "halle": {
        "name": "Halle",
        "beschreibung": "Du stehst in einer dunklen Halle mit moderigem Geruch.",
        "ausgaenge": {
            "norden": "bibliothek",
            "osten": "krypta",
        },
        "gegenstaende": ["Fackel"],
    },
    "bibliothek": {
        "name": "Bibliothek",
        "beschreibung": "Staubige Regale voller zerfledderter Bücher umgeben Dich.",
        "ausgaenge": {
            "sueden": "halle",
        },
        "gegenstaende": ["Schlüssel"],
    },
    "krypta": {
        "name": "Krypta",
        "beschreibung": "Feuchtigkeit glänzt auf den Wänden der alten Krypta.",
        "ausgaenge": {
            "westen": "halle",
        },
        "gegenstaende": [],
    },
}

print("Du stehst vor dem rostigen Tor eines vergessenen Verlieses.")
print("Ein kalter Luftzug weht Dir entgegen.")

spieler = {
    "name": input("Wie heißt Du, Abenteurer? ").strip(),
    "hp": 100,
    "max_hp": 100,
    "gold": int(input("Wie viele Goldmünzen bringst Du mit? ")),
    "inventar": [],
}

ort = "halle"
besuchte_raeume = set()
raum_anzeigen = True

print()
print(f"Willkommen, {spieler['name']}!")
print("Befehle: umsehen, nimm, inventar, status, ende")

while True:
    raum = raeume[ort]

    if raum_anzeigen:
        besuchte_raeume.add(ort)

        print()
        print(raum["beschreibung"])
        print("Ausgänge:", ", ".join(raum["ausgaenge"]))

        raum_anzeigen = False

    befehl = input("\n> ").strip().lower()

    if befehl == "ende":
        print("Du verlässt das Verlies. Bis zum nächsten Mal.")
        break

    elif befehl == "umsehen":
        if raum["gegenstaende"]:
            print("Hier liegt:", ", ".join(raum["gegenstaende"]))
        else:
            print("Hier liegt nichts Brauchbares.")

    elif befehl == "nimm":
        if raum["gegenstaende"]:
            gefundene_gegenstaende = ", ".join(raum["gegenstaende"])

            spieler["inventar"].extend(raum["gegenstaende"])
            raum["gegenstaende"].clear()

            print(f"Du nimmst: {gefundene_gegenstaende}")
        else:
            print("Hier gibt es nichts zu nehmen.")

    elif befehl == "inventar":
        if spieler["inventar"]:
            print("Inventar:", ", ".join(spieler["inventar"]))
        else:
            print("Dein Inventar ist leer.")

    elif befehl == "status":
        print(
            f"{spieler['name']} | "
            f"HP: {spieler['hp']}/{spieler['max_hp']} | "
            f"Gold: {spieler['gold']}"
        )
        print(f"Besuchte Räume: {len(besuchte_raeume)}")
        print(f"Gegenstände: {len(spieler['inventar'])}")

    elif befehl in raum["ausgaenge"]:
        ort = raum["ausgaenge"][befehl]
        raum_anzeigen = True

    else:
        print("Das geht hier nicht.")
        print("Mögliche Ausgänge:", ", ".join(raum["ausgaenge"]))
        print("Weitere Befehle: umsehen, nimm, inventar, status, ende")

Führe das Programm aus:

python dungeon.py

Ein möglicher Durchlauf sieht so aus:

Du stehst vor dem rostigen Tor eines vergessenen Verlieses.
Ein kalter Luftzug weht Dir entgegen.
Wie heißt Du, Abenteurer? Karl
Wie viele Goldmünzen bringst Du mit? 50

Willkommen, Karl!
Befehle: umsehen, nimm, inventar, status, ende

Du stehst in einer dunklen Halle mit moderigem Geruch.
Ausgänge: norden, osten

> umsehen
Hier liegt: Fackel

> nimm
Du nimmst: Fackel

> norden

Staubige Regale voller zerfledderter Bücher umgeben Dich.
Ausgänge: sueden

> nimm
Du nimmst: Schlüssel

> inventar
Inventar: Fackel, Schlüssel

> status
Karl | HP: 100/100 | Gold: 50
Besuchte Räume: 2
Gegenstände: 2

> ende
Du verlässt das Verlies. Bis zum nächsten Mal.

Was im neuen Game-Loop passiert

Der Player State wird nun in einem Dictionary gespeichert:

spieler = {
    "name": input("Wie heißt Du, Abenteurer? ").strip(),
    "hp": 100,
    "max_hp": 100,
    "gold": int(input("Wie viele Goldmünzen bringst Du mit? ")),
    "inventar": [],
}

Das Inventar ist eine Liste innerhalb dieses Dictionaries:

spieler["inventar"]

Der aktuelle Raum wird weiterhin über die Variable ort gespeichert:

ort = "halle"

Mit diesem Wert holen wir das Dictionary des aktuellen Raums aus der Welt:

raum = raeume[ort]

Enthält ort den String "halle", entspricht das diesem Zugriff:

raum = raeume["halle"]

Die Variable raum verweist anschließend auf das Dictionary der Halle.

Bereits besuchte Räume speichern

Das Set besuchte_raeume beginnt leer:

besuchte_raeume = set()

Sobald ein Raum angezeigt wird, fügen wir seine ID hinzu:

besuchte_raeume.add(ort)

Besucht der Spieler denselben Raum später erneut, bleibt er trotzdem nur einmal im Set. Mit len(...) erhalten wir deshalb die Anzahl der unterschiedlichen Räume, die der Spieler bereits betreten hat.

Räume nur nach einem Wechsel anzeigen

Die bool-Variable raum_anzeigen bestimmt, ob die Beschreibung des aktuellen Raums ausgegeben werden soll:

raum_anzeigen = True

Nach der Ausgabe setzen wir sie auf False:

raum_anzeigen = False

Dadurch wird der Raum nicht nach jedem Befehl erneut beschrieben.

Wechselt der Spieler in einen anderen Raum, setzen wir sie wieder auf True:

ort = raum["ausgaenge"][befehl]
raum_anzeigen = True

Beim nächsten Durchlauf des Game-Loops erscheint dann die Beschreibung des neuen Raums.

Gültige Richtungen erkennen

Dieser Vergleich prüft, ob der Input einem Schlüssel im Dictionary der Ausgänge entspricht:

elif befehl in raum["ausgaenge"]:

Befindet sich der Spieler in der Halle, enthält das Dictionary diese Schlüssel:

{
    "norden": "bibliothek",
    "osten": "krypta",
}

Die Befehle "norden" und "osten" sind dort deshalb gültig.

Das Ziel der gewählten Richtung erhalten wir mit:

ort = raum["ausgaenge"][befehl]

Gibt der Spieler "norden" ein, entspricht das:

ort = raum["ausgaenge"]["norden"]

Der neue Wert von ort lautet anschließend "bibliothek".

Die Bewegung durch den Dungeon benötigt damit keine eigene Bedingung mehr für jede Richtung. Sie besteht nur noch aus einem Dictionary-Zugriff.

Gegenstände aufnehmen

Die Gegenstände des aktuellen Raums liegen in einer Liste:

raum["gegenstaende"]

Mit extend(...) übernehmen wir alle Elemente dieser Liste in das Inventar:

spieler["inventar"].extend(raum["gegenstaende"])

Danach leeren wir die Liste des Raums:

raum["gegenstaende"].clear()

Die Gegenstände befinden sich nun im Inventar und können nicht erneut aufgenommen werden.

Listen als String ausgeben

Die Methode join(...) verbindet mehrere Strings zu einem gemeinsamen String:

", ".join(spieler["inventar"])

Aus

["Fackel", "Schlüssel"]

wird dadurch:

Fackel, Schlüssel

Der String vor .join(...) bestimmt, was zwischen die einzelnen Elemente gesetzt wird. In diesem Fall ist es ein Komma mit anschließendem Leerzeichen.

join(...) funktioniert nur, wenn alle Elemente Strings sind. Eine Liste mit Zahlen müsste vorher entsprechend umgewandelt werden.

Daten und Logik trennen

Der wichtigste Fortschritt dieses Teils ist nicht eine einzelne Methode, sondern die neue Struktur des Programms.

Die Räume werden als Daten beschrieben:

raeume = {
    ...
}

Der Game-Loop enthält dagegen die Logik, die mit diesen Daten arbeitet:

while True:
    ...

Möchtest Du einen weiteren Raum hinzufügen, musst Du nicht mehr den kompletten Game-Loop verändern. Du ergänzt lediglich das Dictionary raeume und verbindest den neuen Raum über die Ausgänge mit der vorhandenen Welt.

Diese Trennung macht das Programm übersichtlicher und erleichtert spätere Erweiterungen.

Stolperfallen

  • Bei 0 zu zählen vergessen: Eine Liste mit drei Elementen besitzt die Indizes 0, 1 und 2. Ein Zugriff auf inventar[3] führt zu einem IndexError.

  • Einen fehlenden Dictionary-Schlüssel verwenden: Ein Zugriff wie raum["monster"] führt zu einem KeyError, wenn der Schlüssel nicht existiert. Mit raum.get("monster") erhältst Du stattdessen None.

  • Liste und Dictionary verwechseln: Auf Listen greifst Du mit numerischen Indizes zu, auf Dictionaries mit ihren Schlüsseln.

python inventar[0] raum["beschreibung"]

  • append(...) und extend(...) verwechseln: append(...) fügt einen Wert hinzu. extend(...) übernimmt die einzelnen Elemente einer anderen Collection.

  • Ein leeres Set mit {} anlegen: {} erzeugt ein leeres Dictionary. Für ein leeres Set benötigst Du set().

  • Von einer festen Set-Reihenfolge ausgehen: Sets besitzen keine für Deine Programmlogik verlässliche Reihenfolge. Verwende eine Liste, wenn die Position der Elemente wichtig ist.

  • Zahlen direkt mit join(...) verbinden: join(...) erwartet Strings. ", ".join([1, 2, 3]) führt deshalb zu einem TypeError.

  • Umlaute in Befehlen übersehen: Im Code verwenden wir für die südliche Richtung bewusst "sueden", damit sie ohne Umlaut eingegeben werden kann.

Übungen

1. Eine Schatzkammer ergänzen

Füge dem Verlies einen vierten Raum namens "schatzkammer" hinzu. Er soll von der Bibliothek aus nach Norden erreichbar sein und einen "Goldhaufen" enthalten.

Lösung Ergänze das Dictionary `raeume` um folgenden Eintrag:
"schatzkammer": {
    "name": "Schatzkammer",
    "beschreibung": "Gold und Edelsteine glitzern im Licht Deiner Fackel.",
    "ausgaenge": {
        "sueden": "bibliothek",
    },
    "gegenstaende": ["Goldhaufen"],
},
Erweitere außerdem die Ausgänge der Bibliothek:
"ausgaenge": {
    "sueden": "halle",
    "norden": "schatzkammer",
},
Die Bibliothek führt nun nach Norden in die Schatzkammer. Von dort gelangt der Spieler nach Süden zurück zur Bibliothek.

2. Einen Startgegenstand hinzufügen

Gib dem Spieler zu Beginn ein Stück Brot. Das Inventar soll also nicht mehr leer starten.

Lösung Ändere den Eintrag `inventar` im Player State:
spieler = {
    "name": input("Wie heißt Du, Abenteurer? ").strip(),
    "hp": 100,
    "max_hp": 100,
    "gold": int(input("Wie viele Goldmünzen bringst Du mit? ")),
    "inventar": ["Brot"],
}
Der Befehl `inventar` zeigt das Brot anschließend direkt an.

3. Alle Ausgänge eines Raums ausgeben

Schreibe eine for-Schleife, die alle Richtungen und Zielräume der Halle ausgibt.

Die Ausgabe soll ungefähr so aussehen:

norden -> bibliothek
osten -> krypta
Lösung
halle = raeume["halle"]

for richtung, ziel in halle["ausgaenge"].items():
    print(f"{richtung} -> {ziel}")
`.items()` liefert bei jedem Durchlauf ein Paar aus Schlüssel und Wert. Dieses Paar wird direkt in die Variablen `richtung` und `ziel` entpackt.

Im nächsten Teil

Die Welt ist jetzt sauber als Daten beschrieben. Der Game-Loop ist durch die vielen Befehle allerdings weiterhin recht lang.

In Teil 5 lernst Du deshalb Funktionen kennen. Damit zerlegen wir die Logik in benannte, wiederverwendbare Blöcke und machen den Game-Loop wieder übersichtlich.

0 Kommentare

Noch keine Kommentare. Sei der/die Erste!