← Teacher guide

A text adventure

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.

Deeper Python in the console · Errors and the adventure · about 60 minutes · Open the student lesson

Classroom resources

Load these into Waypoints Prepare: choose Load URL and paste the copied address, or download the file and import it. In Prepare, the worksheet's Teacher edition shows the answers; the student copy hides them.

Students will be able to

Timing

Predict, Run and Investigate 15 min · Milestone 1 15 min · Milestone 2 10 min · Milestone 3 20 min

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

What you will seeA question to askThe fix
Taking something says it is not here, though look shows it.Is the word being checked exactly as stored (small letters)?The command is lowered; store things in small letters too.
The shed opens without the key.Does the check happen before here = ... changes?Check where the way leads first, and only move if it is allowed.
The win message never appears.Is the win check inside the loop, after the commands?Put if "crown" in bag: at the end of the loop body, with break.

Answers to the checks

  1. Predict: Which room does it print?
    shed. north to the kitchen, south back to the hall, west is not an exit from the hall (so nothing happens), east to the garden, east again to the shed.
  2. Where things are: What is things["kitchen"]?
    ["key"], the list of things lying in the kitchen
    things maps each room to a list of what lies there. Taking something moves it from that list to the bag.
  3. A new command: Where would a new command, such as help, go?
    A new elif branch in the loop
    Each command is one branch: elif command == "help": and what it prints. The else at the end catches anything not understood.
  4. Where this leads: Your adventure forgets everything when the program stops. What would it need to save the game?
    To write here and bag to a file, and read them back next time
    Saving means writing the game's state to a file and reading it back. That is the next step in this course: files.

Solutions

One possible solution for each task. Other correct programs are fine.

Milestone 1: take and drop

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.")
    elif len(words) == 2 and words[0] == "take":
        if words[1] in things[here]:
            things[here].remove(words[1])
            bag.append(words[1])
            print("You take the " + words[1] + ".")
        else:
            print("There is no " + words[1] + " here.")
    elif len(words) == 2 and words[0] == "drop":
        if words[1] in bag:
            bag.remove(words[1])
            things[here].append(words[1])
            print("You drop the " + words[1] + ".")
        else:
            print("You don't have a " + words[1] + ".")
    else:
        print("Try: look, go north, take lamp, drop lamp, bag or quit")

Milestone 2: a locked shed

rooms = {
    "hall": "A long hall with a creaky floor.",
    "kitchen": "A kitchen that smells of toast.",
    "garden": "A garden full of tall grass. A locked shed stands to the east.",
    "shed": "A dusty shed. Something glints in the corner.",
}
exits = {
    "hall": {"north": "kitchen", "east": "garden"},
    "kitchen": {"south": "hall"},
    "garden": {"west": "hall", "east": "shed"},
    "shed": {"west": "garden"},
}
things = {
    "hall": ["lamp"],
    "kitchen": ["key"],
    "garden": [],
    "shed": ["crown"],
}
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]:
            if exits[here][words[1]] == "shed" and "key" not in bag:
                print("The shed is locked.")
            else:
                here = exits[here][words[1]]
                print(rooms[here])
        else:
            print("You can't go that way.")
    elif len(words) == 2 and words[0] == "take":
        if words[1] in things[here]:
            things[here].remove(words[1])
            bag.append(words[1])
            print("You take the " + words[1] + ".")
        else:
            print("There is no " + words[1] + " here.")
    elif len(words) == 2 and words[0] == "drop":
        if words[1] in bag:
            bag.remove(words[1])
            things[here].append(words[1])
            print("You drop the " + words[1] + ".")
        else:
            print("You don't have a " + words[1] + ".")
    else:
        print("Try: look, go north, take lamp, drop lamp, bag or quit")

Milestone 3: make it yours › Win

rooms = {
    "hall": "A long hall with a creaky floor.",
    "kitchen": "A kitchen that smells of toast.",
    "garden": "A garden full of tall grass. A locked shed stands to the east.",
    "shed": "A dusty shed. Something glints in the corner.",
}
exits = {
    "hall": {"north": "kitchen", "east": "garden"},
    "kitchen": {"south": "hall"},
    "garden": {"west": "hall", "east": "shed"},
    "shed": {"west": "garden"},
}
things = {
    "hall": ["lamp"],
    "kitchen": ["key"],
    "garden": [],
    "shed": ["crown"],
}
bag = []
here = "hall"
moves = 0
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]:
            if exits[here][words[1]] == "shed" and "key" not in bag:
                print("The shed is locked.")
            else:
                here = exits[here][words[1]]
                moves += 1
                print(rooms[here])
        else:
            print("You can't go that way.")
    elif len(words) == 2 and words[0] == "take":
        if words[1] in things[here]:
            things[here].remove(words[1])
            bag.append(words[1])
            print("You take the " + words[1] + ".")
        else:
            print("There is no " + words[1] + " here.")
    elif len(words) == 2 and words[0] == "drop":
        if words[1] in bag:
            bag.remove(words[1])
            things[here].append(words[1])
            print("You drop the " + words[1] + ".")
        else:
            print("You don't have a " + words[1] + ".")
    else:
        print("Try: look, go north, take lamp, drop lamp, bag or quit")
    if "crown" in bag:
        print("You found the crown in " + str(moves) + " moves. You win!")
        break

Milestone 3: make it yours › Your own touch

Open task: any program that does what the step asks.

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.

Exit ticket

Which data structure would you use for the things in each room, and why?

A dictionary from room name to a list: look up the room by name, and the list grows and shrinks as things are taken and dropped.

What each step is for

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.