Text Adventure Game in Python
Intermediate · Games · about 90 minutes to build · 146 lines
You wake up in a locked house and type commands such as go north and take lamp to explore the rooms and find a way out.
The program
Press Run and answer its questions in the box that appears under the code.
Open it in the full editor to save it, share it and keep building.
Sample run
What the program printed, and what was typed (in yellow).
=== The Locked House === You wake up inside an old house. Find a way out. Type help for the commands. You are in a dusty hall. The front door is to the south. Exits: north, east, south > go south The door is locked. > n You are in a library with tall shelves of old books. You see: note Exits: south > take note You take the note. It says: 'Spare key in the cellar. Take a light, the bulb is gone.' > s You are in a dusty hall. The front door is to the south. Exits: north, east, south > e You are in a cold kitchen. Stone steps lead down. You see: lamp Exits: west, down > down It is too dark to see anything here. Exits: up > take key You feel around in the dark but find nothing. > up You are in a cold kitchen. Stone steps lead down. You see: lamp Exits: west, down > take lamp You take the lamp. An oil lamp, still burning. It will light up a dark room. > dance I don't understand that. Type help for the commands. > d You are in a damp cellar full of empty jars. You see: key Exits: up > take key You take the key. A heavy iron key with a tag: FRONT DOOR. > u You are in a cold kitchen. Stone steps lead down. Exits: west, down > w You are in a dusty hall. The front door is to the south. Exits: north, east, south > s You unlock the door with the key. You are in the garden, under an open sky. You escaped in 15 moves. Well done.
How it is built
1. The map as data
Each room is a dictionary with a description, its exits, its items and optional flags such as dark and locked. A function builds the map, so every new game starts with the items back in their rooms.
def new_world(): """Return a fresh map of the rooms, so every game starts the same way.""" return { "hall": { "description": "a dusty hall. The front door is to the south.", "exits": {"north": "library", "east": "kitchen", "south": "outside"}, "locked": {"south": "key"}, # this exit needs the key "items": [], }, "library": { "description": "a library with tall shelves of old books.", "exits": {"south": "hall"}, "items": ["note"], }, "kitchen": { "description": "a cold kitchen. Stone steps lead down.", "exits": {"west": "hall", "down": "cellar"}, "items": ["lamp"], }, "cellar": { "description": "a damp cellar full of empty jars.", "exits": {"up": "kitchen"}, "items": ["key"], "dark": True, }, "outside": { "description": "the garden, under an open sky.", "exits": {}, "items": [], }, }
2. Describing a room
can_see returns False in a dark room unless the lamp is in the inventory, and describe then hides what is there. That one rule is the puzzle: the key lies in the dark cellar.
def describe(room, inventory): """Print where the player is, what is here and the ways out.""" if can_see(room, inventory): print(f"You are in {room['description']}") if room["items"]: print(f"You see: {', '.join(room['items'])}") else: print("It is too dark to see anything here.") if room["exits"]: print(f"Exits: {', '.join(room['exits'])}")
3. Moving between rooms
SHORTCUTS turns n into north. A locked exit names the item it needs, and the player gets through only with that item in the inventory.
def go(world, current, direction, inventory): """Return the room the player ends up in after trying to move.""" room = world[current] direction = SHORTCUTS.get(direction, direction) if not direction: print("Go where? For example: go north") return current if direction not in room["exits"]: print("You can't go that way.") return current needed = room.get("locked", {}).get(direction) if needed and needed not in inventory: print("The door is locked.") return current if needed: print(f"You unlock the door with the {needed}.") describe(world[room["exits"][direction]], inventory) return room["exits"][direction]
4. Picking things up
Taking an item removes it from the room's list and appends it to the inventory list, then prints what the player reads on it.
def take(room, item, inventory): """Move an item from the room into the inventory, if the player can see it.""" if not item: print("Take what? For example: take lamp") elif not can_see(room, inventory): print("You feel around in the dark but find nothing.") elif item in room["items"]: room["items"].remove(item) inventory.append(item) print(f"You take the {item}. {ITEM_TEXT[item]}") else: print(f"There is no {item} here.")
5. Reading commands
Each command is split into words: the first is the verb and the rest is the noun, so take lamp gives verb = "take" and noun = "lamp". The loop runs until the player reaches the garden or types quit.
def play_game(): """Play until the player gets outside or quits; return the moves taken.""" world = new_world() current = START_ROOM inventory = [] moves = 0 describe(world[current], inventory) while current != "outside": # "take the lamp" and "take lamp" mean the same, so drop "the". words = [w for w in input("\n> ").lower().split() if w != "the"] if not words: continue verb, noun = words[0], " ".join(words[1:]) moves += 1 if verb in SHORTCUTS or verb in SHORTCUTS.values(): current = go(world, current, verb, inventory) elif verb == "go": current = go(world, current, noun, inventory) elif verb == "take": take(world[current], noun, inventory) elif verb in ("inventory", "i"): print(f"You carry: {', '.join(inventory) or 'nothing'}") elif verb == "look": describe(world[current], inventory) elif verb == "help": print(HELP) elif verb == "quit": return None else: print("I don't understand that. Type help for the commands.") return moves
Make it bigger
- Add a drop command that moves an item from the inventory back into the room.
- Put a second puzzle in the library, such as a book that has to be read to learn a code.
- Add a save command that writes the current room and inventory to a file.
- Give the lamp a limited amount of oil, so it goes out after 20 moves.
What it practises
- nested dictionaries
- parsing commands with split
- lists as an inventory
- functions