← Deeper Python in the console

A text adventure

Put the parser, the bag and the map together into a text adventure, in three milestones, then make it your own.

Text adventures were among the first computer games, and the same pieces (a parser, an inventory, a map) still sit under every game.

You have built every piece: a parser that tidies and splits commands, a bag that grows and shrinks, and a map of rooms in dictionaries. Now put them together into a text adventure, in three milestones. Each milestone starts from a working game; the last one is yours.

PredictRead the code and say what it will do before you run anything. A wrong guess is fine: it shows you what to look for.

Predict

adventure.py
exits = {
    "hall": {"north": "kitchen", "east": "garden"},
    "kitchen": {"south": "hall"},
    "garden": {"west": "hall", "east": "shed"},
}
here = "hall"
for move in ["north", "south", "west", "east", "east"]:
    if move in exits[here]:
        here = exits[here][move]
print(here)

Follow the moves through the map, one at a time. A move that is not an exit from the room you are in does nothing.

Which room does it print?

RunRun it and compare the result with your prediction.

Run

Play it: look, go north, look, bag. You can see things but not take them yet. Each command is one branch of the if / elif chain inside the loop.

adventure.py
rooms = {
    "hall": "A long hall with a creaky floor.",
    "kitchen": "A kitchen that smells of toast.",
    "garden": "A garden full of tall grass.",
}
exits = {
    "hall": {"north": "kitchen", "east": "garden"},
    "kitchen": {"south": "hall"},
    "garden": {"west": "hall"},
}
things = {
    "hall": ["lamp"],
    "kitchen": ["key"],
    "garden": [],
}
bag = []
here = "hall"
print(rooms[here])
while True:
    command = input("> ").strip().lower()
    words = command.split()
    if command == "quit":
        break
    elif command == "look":
        print(rooms[here])
        print("Exits: " + ", ".join(exits[here]))
        if len(things[here]) > 0:
            print("You see: " + ", ".join(things[here]))
    elif command == "bag":
        print("You carry: " + ", ".join(bag))
    elif len(words) == 2 and words[0] == "go":
        if words[1] in exits[here]:
            here = exits[here][words[1]]
            print(rooms[here])
        else:
            print("You can't go that way.")
    # Milestone 1: take and drop go here.
    else:
        print("Try: look, go north, take lamp, drop lamp, bag or quit")
Try it yourselfYour edits are saved in this browser.

InvestigateTrace how each line of code connects to what you see.

Investigate

adventure.py
rooms = {
    "hall": "A long hall with a creaky floor.",
    "kitchen": "A kitchen that smells of toast.",
    "garden": "A garden full of tall grass.",
}
exits = {
    "hall": {"north": "kitchen", "east": "garden"},
    "kitchen": {"south": "hall"},
    "garden": {"west": "hall"},
}
things = {
    "hall": ["lamp"],
    "kitchen": ["key"],
    "garden": [],
}
bag = []
here = "hall"
print(rooms[here])
while True:
    command = input("> ").strip().lower()
    words = command.split()
    if command == "quit":
        break
    elif command == "look":
        print(rooms[here])
        print("Exits: " + ", ".join(exits[here]))
        if len(things[here]) > 0:
            print("You see: " + ", ".join(things[here]))
    elif command == "bag":
        print("You carry: " + ", ".join(bag))
    elif len(words) == 2 and words[0] == "go":
        if words[1] in exits[here]:
            here = exits[here][words[1]]
            print(rooms[here])
        else:
            print("You can't go that way.")
    # Milestone 1: take and drop go here.
    else:
        print("Try: look, go north, take lamp, drop lamp, bag or quit")

The third dictionary.

Where it would go.

What is things["kitchen"]?

Where would a new command, such as help, go?

ModifyChange the code on purpose and check the result.

Milestone 1: take and drop

Where the comment says Milestone 1, add two branches:

Test: take the lamp, carry it to the kitchen, drop it, look.

Try it yourselfYour edits are saved in this browser.

take moves a thing from the room into the bag, and drop puts it back.

    Run your program. This ticks when your code does all of the above.

    ModifyChange the code on purpose and check the result.

    Milestone 2: a locked shed

    This version adds a shed east of the garden, and take and drop work. Where the comment says Milestone 2, make the shed locked: if the way leads to "shed" and "key" not in bag (not in is the opposite of in), print The shed is locked. and stay where you are.

    Try it yourselfYour edits are saved in this browser.

    The shed only opens when the key is in the bag.

      Run your program. This ticks when your code does all of the above.

      MakeBuild something new using what you have learned. Expect a few tries; that is normal.

      Milestone 3: make it yours

      The game ends with a win message when the crown is in the bag.

      Need a hint?

      After the if / elif chain, still inside the loop: if "crown" in bag:, print a win message and break.

      Add something of your own: a room, a thing to find, a help command, a move counter, a second locked door.

      Try it yourselfYour edits are saved in this browser.

      Win when the crown is in the bag.

        Run your program. This ticks when your code does all of the above.

        Info

        Where this leads

        Every game, from text adventures to open worlds, is this shape: a loop that reads input, rules that change the world, and the world stored as data. Next in this course: saving and loading the game with a file.

        Your adventure forgets everything when the program stops. What would it need to save the game?

        For teachers

        Students combine the parser, the bag and the map into a text adventure in three milestones (take and drop, a locked shed, a win plus a change of their own), each starting from a working game. About 60 minutes.

        Open the teacher guide: answers, solutions, slides and a worksheet.

        Students will be able to

        Before class

        Read the unit primers first. The game keeps three dictionaries: rooms (descriptions), exits (each room's dictionary of directions) and things (each room's list of things). here is the current room and bag is a list. Each command is one elif branch inside while True. Milestone 1 moves a thing between things[here] and bag; Milestone 2 adds one check ("key" not in bag; not in is the opposite of in, said once in the text); Milestone 3 is goals only. Each milestone starts from a working version, so a student stuck on one can still do the next.

        Running the lesson

        1. Predict: draw the map on the board and walk it with a counter.
        2. Run: ask students to find the branch that handles each command.
        3. Milestone 1: the item moves between two lists. Act it out with a real object and two boxes.
        4. Milestone 2: ask where the check must go: before here changes.
        5. Milestone 3: pair students to play each other's adventures and report one bug.

        The hard part (on purpose)

        Holding several parts in mind: the loop, the parser, the rooms and the bag. Point them back to the milestone list and have them test one command at a time.

        When students get stuck

        Stretch and support

        Stretch: Add a second item that must be used somewhere (light the lamp in a dark cellar), or a timer of moves that ends the game.

        Support: Give students the take branch for Milestone 1 and ask them to write drop by mirroring it.

        Where it leads

        Saving and loading the game is the next part of this course (files, then try for a missing file). Game engines use the same shape: a loop, input, rules and the world as data.

        What each step is for

        Now you can
        Next lesson6. Save the gameWrite the game to a file with json, read it back next time, and catch the error when there is no file yet.