Intermediate project · Medium
Text adventure driven by data
You write a text adventure the player moves through by typing commands like go north, look, take lamp and inventory. The interesting constraint is that the world is data: rooms, their descriptions, their exits and their items all live in one structure, and the game loop knows nothing about any particular room. Adding a room becomes adding a dictionary entry, and that difference is the whole lesson.
- Languages
- Python
- Size
- Medium: a few sessions
- Where to build it
- Runs as-is in the Python playground: put your commands in the input box, one per line, ending with quit. Open the Python playground →
What you will practise
- nested dictionaries and lists as a data model
- separating a game loop from the data it walks over
- parsing a command into a verb and a target
- dispatching to handlers from a dictionary instead of a long if chain
- validating your own data at startup
- designing error messages that keep a program running
Requirements
The project is done when every one of these is true.
- The world is one data structure mapping a room id to its name, description, exits and items, and the game loop contains no room names or room-specific logic at all.
- There are at least five rooms, and at least one two-way connection and one one-way connection between them.
- The commands go, look, take, drop, inventory, help and quit all work, and the set of commands is defined in one place so adding one does not mean editing the loop.
- Commands are case-insensitive and tolerate extra spaces, so GO NORTH behaves the same as go north.
- Trying to move through an exit that does not exist prints a message naming the directions that do exist and leaves the player where they were.
- Taking an item moves it out of the room and into the inventory, so taking it a second time says it is not there, and dropping it puts it back in the current room.
- An unrecognised command prints a message that repeats what you typed and suggests help, and the game carries on.
- A startup check walks every room's exits and raises a clear error if any of them names a room that does not exist, before the player sees anything.
- There is one goal condition, defined in the data or in a single function, and reaching it prints a closing message and ends the loop.
Milestones
A sensible order to build it in, so something works at every step.
Write the world data and print one room
Define three rooms with descriptions and exits, set a current room, and print its name, description, items and available exits. No loop yet.
Add the loop and the go command
Read a line, split it into a verb and the rest, and make go change the current room by looking the direction up in that room's exits.
Handle every way go can fail
Cover go with no direction, a direction this room does not have, and a direction that is not a direction at all, each with its own message and no change of room.
Add items, take and drop, and inventory
Give rooms item lists, add a player inventory list, and make take and drop move an item between exactly one of each so it can never exist in two places.
Replace the if chain with a dispatch table
Turn each command into a function with the same shape, put them in a dictionary keyed by verb, and let the loop look the verb up and call it.
Add the startup validation and the goal
Before the first prompt, check every exit target and every item is well formed, then add the win condition and its closing message.
Grow the world to five rooms and play it
Add the remaining rooms purely as data, then play it end to end without touching the loop, which is the proof the design worked.
Hints
Open one only when you are stuck. Each gives a little more away.
Show hint 1Hint 1
If your loop mentions a room by name, the world is not really in the data yet. Every decision the loop makes should come from looking something up.
Show hint 2Hint 2
Splitting a command once, into the first word and everything after it, handles both go north and take brass lamp without a parser.
Show hint 3Hint 3
A dictionary from verb to function is a dispatch table. Give every handler the same parameters, even when one of them ignores an argument, and the loop stays four lines long.
Show hint 4Hint 4
Keep the inventory and the room item lists as the only two places an item can be, and always remove before you add. An item that appears twice is a sign you copied instead of moving.
Show hint 5Hint 5
Validate the world before the game starts, not when the player walks into the bug. A loop over every room and every exit target is a few lines and saves an evening.
Show hint 6Hint 6
In the playground, commands come from the input box one line at a time and reading past the last line raises an error. Put a quit on the last line, or catch that error and end the game.
How to test it
Run these checks yourself, or turn them into automated tests once you know how.
- From the starting room, go north then go south should return you to the starting room and print its description again.
- Type go up in a room with no up exit: expect a message listing the real exits, and the next look should show you are still in the same room.
- Take an item, then take it again: the second attempt should say it is not here, and inventory should list it exactly once. Now drop it and look: it should be back in the room's list and gone from the inventory.
- Type xyzzy: expect a message repeating xyzzy and pointing at help, and the game should still accept the next command.
- Type go with nothing after it: expect a message asking which direction, not an error about an index or a key.
- Deliberately point one exit at a room id that does not exist and run the game: the startup check should refuse to start and name the bad exit.
- Write down a sequence of commands that wins, run exactly that sequence, and confirm it ends with the closing message and stops reading commands.
- Add a sixth room and connect it, without editing the game loop at all. If you had to touch the loop, the data model needs another pass.
Stretch goals
- Add locked exits that need a specific item in the inventory, with the requirement stored in the data.
- Add a move counter and a limit, so the player has to find an efficient route.
- Add save and load using JSON, storing only the current room and the inventory rather than the whole world.
- Add items that can be used on other items, with the results in a data table rather than in code.
- Add a command that prints a rough map of rooms visited so far, built from the exits data.