Project · after Level 4

Text Adventure Engine 🗺️

Build the engine for a text adventure: rooms you move between, items you pick up, a parser that understands commands. It is the purest showcase there is for dictionaries as a world and match for a command parser, and it is genuinely fun to extend.

Difficulty 🐍🐍🐍🐍🐍

📋 Build this

  • Describe the world as a dictionary of rooms, each with exits and items.
  • Parse commands: look, go <direction>, take <item>, inventory.
  • Track the player's location and inventory as they play.
  • Reject impossible moves and unknown commands gracefully.

Hints, if you want them

Try the spec cold first. Open a hint only when you are properly stuck; the struggle is where the learning is.

Hint 1: The world as data
Each room is {"description": ..., "exits": {"north": "jungle"}, "items": [...]}. The whole map is a dict of rooms keyed by name (Lesson 15).
Hint 2: Parsing with match
match command.lower().split(): with cases like case ["go", direction]: reads beautifully and handles the whole parser (Lesson 33). Put the specific cases before the catch-all.
Hint 3: State
Two variables, here (the current room name) and carrying (a list), are the entire game state. Move by reassigning here.

The reference solution

Yours does not need to match this. There are many good ways to build any of these. Compare only after you have your own working.

Reveal the reference solution
ROOMS = {
    "beach": {"description": "A beach. A rubber chicken lies in the sand.",
              "exits": {"north": "jungle"}, "items": ["rubber chicken"]},
    "jungle": {"description": "Thick jungle. Something rustles.",
               "exits": {"south": "beach", "east": "clearing"}, "items": []},
    "clearing": {"description": "A clearing with a locked chest.",
                 "exits": {"west": "jungle"}, "items": ["chest"]},
}


def play(commands):
    here = "beach"
    carrying = []
    out = []

    for command in commands:
        room = ROOMS[here]
        match command.lower().split():
            case ["look"]:
                out.append(room["description"])
                if room["items"]:
                    out.append("You see: " + ", ".join(room["items"]))
            case ["go", direction] if direction in room["exits"]:
                here = room["exits"][direction]
                out.append(f"You go {direction}. {ROOMS[here]['description']}")
            case ["go", direction]:
                out.append(f"You cannot go {direction}.")
            case ["take", *words] if " ".join(words) in room["items"]:
                item = " ".join(words)
                room["items"].remove(item)
                carrying.append(item)
                out.append(f"Taken: {item}.")
            case ["inventory"] | ["i"]:
                out.append("Carrying: " + (", ".join(carrying) or "nothing"))
            case _:
                out.append(f"I do not understand {command!r}.")

    return out


for line in play(["look", "take rubber chicken", "inventory",
                  "go north", "go up", "go east", "look"]):
    print(line)
A beach. A rubber chicken lies in the sand.
You see: rubber chicken
Taken: rubber chicken.
Carrying: rubber chicken
You go north. Thick jungle. Something rustles.
You cannot go up.
You go east. A clearing with a locked chest.
A clearing with a locked chest.
You see: chest

Stretch goals

+250 XP