Building a game is one of the most effective ways to learn Python. In this tutorial you will build a fully playable text-based adventure game from scratch — no libraries, no frameworks, just the Python you learn along the way.
Text-based adventure games have been teaching programmers the fundamentals for decades. Every room you walk into is a function call. Every choice the player makes is a conditional branch. Every item in the inventory is a data structure. By the time you finish this tutorial, you will have written real, working Python and understood exactly why each piece behaves the way it does.
What You Will Build
The finished game will have five rooms connected by directional exits (north, south, east, west). The player can pick up items, check their inventory, and reach a win condition by arriving at the correct room while carrying a specific item. The entire game runs in a terminal and requires nothing beyond a standard Python 3 installation.
Here is the complete game in one file so you can see where you are headed before you start building it piece by piece:
# adventure.py — complete text adventure game
rooms = {
"entrance": {
"description": "You stand at the entrance to a crumbling fortress.",
"exits": {"north": "courtyard"},
"items": ["torch"]
},
"courtyard": {
"description": "A wide courtyard, open to the grey sky. Passages lead in three directions.",
"exits": {"south": "entrance", "east": "armory", "west": "garden"},
"items": []
},
"armory": {
"description": "Racks of rusted weapons line the walls. One blade still gleams.",
"exits": {"west": "courtyard"},
"items": ["sword"]
},
"garden": {
"description": "An overgrown garden. Vines cover a locked iron door to the north.",
"exits": {"east": "courtyard", "north": "vault"},
"items": ["key"]
},
"vault": {
"description": "The vault. This is where the treasure is kept.",
"exits": {"south": "garden"},
"items": ["treasure"]
}
}
current_room = "entrance"
inventory = []
def display_room(rooms, room_name):
room = rooms[room_name]
print("\n---")
print(room["description"])
if room["items"]:
print(f"You see: {', '.join(room['items'])}")
exits = ", ".join(room["exits"].keys())
print(f"Exits: {exits}")
print("Welcome to the Fortress. Type a direction to move, 'take [item]' to pick something up,")
print("'inventory' to check your bag, or 'quit' to exit.\n")
while True:
display_room(rooms, current_room)
# Win condition
if current_room == "vault" and "key" in inventory:
print("\nYou unlock the vault with the key. The treasure is yours. You win!")
break
command = input("\n> ").strip().lower()
if command == "quit":
print("Farewell.")
break
elif command in rooms[current_room]["exits"]:
current_room = rooms[current_room]["exits"][command]
elif command.startswith("take "):
item = command[5:]
if item in rooms[current_room]["items"]:
inventory.append(item)
rooms[current_room]["items"].remove(item)
print(f"You pick up the {item}.")
else:
print(f"There is no {item} here.")
elif command == "inventory":
if inventory:
print(f"You are carrying: {', '.join(inventory)}")
else:
print("Your inventory is empty.")
else:
print("That direction is not available. Check your exits.")
Save the code above as adventure.py, open a terminal, navigate to the folder, and run python adventure.py. You need Python 3.6 or later. On some systems the command is python3 adventure.py.
Variables, input(), and Player Interaction
Two lines near the top of the game set up the player's starting state:
current_room = "entrance"
inventory = []
current_room is a string variable that holds the name of the room the player is currently in. inventory is a list variable that starts empty and grows when the player picks up items. Both of these values change as the game runs — that is exactly what variables are for.
Player input arrives through the built-in input() function. When Python reaches that line, execution pauses and waits for the user to type something and press Enter. Whatever the user typed is returned as a string.
command = input("\n> ").strip().lower()
Two string methods are chained after input(). The .strip() call removes any whitespace that accidentally crept in at the start or end of what the user typed. The .lower() call converts everything to lowercase so that typing "North", "NORTH", or "north" all produce the same string: "north". Without these two calls, players would need to type commands in exactly the right case every time.
Dictionaries as room data
The rooms are stored in a dictionary named rooms. A Python dictionary is a collection of key-value pairs wrapped in curly braces. Each room name is a key, and its value is another dictionary containing that room's description, exits, and items. This nested structure means you can look up any room by name and immediately access all of its data.
# Accessing nested dictionary values
room = rooms["courtyard"]
print(room["description"]) # A wide courtyard, open to the grey sky...
print(room["exits"]) # {'south': 'entrance', 'east': 'armory', 'west': 'garden'}
Adding a room is as simple as adding a new key-value pair to the rooms dictionary and making sure at least one existing room has an exit pointing to it. The rest of the code adapts automatically.
Build the correct Python line to read player input, strip whitespace, and convert to lowercase:
command =. Then input("> ") captures the user's text. .strip() must come before .lower() since both are chained left to right. .upper() is a distractor — it converts to uppercase, which is the opposite of what you want. print( is also a distractor — it outputs text, it does not read input.
Conditional Logic with if, elif, and else
Once the player's command is a clean lowercase string, the game needs to decide what to do with it. That decision-making is handled by a chain of if, elif, and else statements.
if command == "quit":
print("Farewell.")
break
elif command in rooms[current_room]["exits"]:
current_room = rooms[current_room]["exits"][command]
elif command.startswith("take "):
item = command[5:]
if item in rooms[current_room]["items"]:
inventory.append(item)
rooms[current_room]["items"].remove(item)
print(f"You pick up the {item}.")
else:
print(f"There is no {item} here.")
elif command == "inventory":
if inventory:
print(f"You are carrying: {', '.join(inventory)}")
else:
print("Your inventory is empty.")
else:
print("That direction is not available. Check your exits.")
Python evaluates each condition top to bottom and runs the first block whose condition is True. Once a block runs, all the remaining elif and else branches are skipped. The final else acts as a catch-all — it runs only when none of the above conditions matched, giving the player a helpful error message instead of silent failure.
Notice the movement branch: command in rooms[current_room]["exits"]. The in keyword checks whether a value exists inside a collection. Here it checks whether the player's typed direction is a key in the current room's exits dictionary. If it is, the game updates current_room to the destination room name — one dictionary lookup moves the player to a new location.
- What happens
- Python raises a SyntaxError because a single = is the assignment operator, not a comparison. You cannot assign inside an if condition this way.
- The fix
- Use == to compare two values:
if command == "quit":
- What happens
- Python raises a SyntaxError immediately. Every if, elif, and else header must end with a colon.
- The fix
- Always end the header line with
:— for example,elif command == "inventory":
- What happens
- Python uses indentation to define blocks. Code that should be inside the if statement runs unconditionally if it is not indented.
- The fix
- Indent every line that belongs inside the block by exactly four spaces (or one tab — but be consistent throughout the file).
- What happens
- Python raises a SyntaxError. The else block must always come last. You cannot add elif branches after it.
- The fix
- Put all elif clauses before the final else. else is the catch-all that only runs when nothing above matched.
The movement handler below has one bug that prevents the player from moving to a new room. Click the line you think is wrong, then hit check.
current_room == destination to current_room = destination. A double equals sign (==) is a comparison — it checks whether the two values are equal and returns True or False, but changes nothing. A single equals sign (=) is assignment — it stores the new value into the variable. Without the fix, the player's location never actually updates.
How to Build a Text-Based Adventure Game in Python
The game is built in five distinct steps. Each step introduces one new concept. Follow them in order and you will have a working game by the end of step five.
-
Define the rooms dictionary
Create a Python dictionary called
rooms. Each top-level key is a room name string such as"entrance". Its value is a nested dictionary with three keys:"description"(a string),"exits"(a dictionary mapping direction strings to destination room names), and"items"(a list of item name strings). Start with two or three rooms and connect them by setting matching exit entries on each side. -
Set up the player state
Below the rooms dictionary, declare two variables:
current_room = "entrance"andinventory = []. These two variables represent the complete state of the player at any point in the game. Everything the game loop does will read or modify one of these two values. -
Write the display_room function
Define a function called
display_room(rooms, room_name). Inside the function, look up the room usingrooms[room_name]and print its description. If the room's items list is not empty, print those too. Finally, join the exit keys into a comma-separated string and print them. Putting this logic in a function means you call it once per loop iteration rather than repeating the same print statements every time. -
Build the main game loop
Write
while True:to start an infinite loop. On each iteration: calldisplay_room, read input withinput("\n> ").strip().lower(), then branch withif/elif/elseto handle the"quit"command (which breaks the loop), movement (updatingcurrent_room), item pickup (appending toinventoryand removing from the room), and the inventory command. -
Add a win condition
Inside the loop, just after the
display_roomcall, add anifstatement that checks two things at once usingand: whethercurrent_roomequals the win room name, and whether the required item isinthe inventory list. When both are true, print a victory message andbreakout of the loop. The game ends cleanly and does not prompt for another command.
"Programs must be written for people to read, and only incidentally for machines to execute." — Abelson and Sussman, Structure and Interpretation of Computer Programs
Python Learning Summary Points
- A dictionary is the natural data structure for a game world. Each room is a key, and its associated description, exits, and items are the value — stored as a nested dictionary. Looking up any piece of room data takes a single expression.
- The
input()function always returns a string. Chaining.strip().lower()immediately after the call normalises player input so your conditions work regardless of how the player capitalises or spaces their commands. - The
while Trueloop combined with strategicbreakstatements is the standard pattern for a terminal game loop. The loop runs indefinitely until a win condition or quit command triggers a break. - Every conditional branch in the game loop corresponds to a real gameplay action. Keeping each action in its own
elifblock makes the code readable and easy to extend without breaking existing behaviour. - Extracting repeated logic into a named function — such as
display_room— eliminates duplication and gives a meaningful name to a chunk of behaviour, which makes the main loop far easier to follow.
The game you have built is intentionally simple, but it already demonstrates every foundational concept Python beginners need: variables, strings, lists, dictionaries, functions, loops, and conditional logic. From here you can extend it with a combat system, locked doors, saving and loading state to a file, or even a simple parser that understands multi-word commands. Every extension will use the same core concepts you just practised.
Frequently Asked Questions
You do not need much prior knowledge. This tutorial is designed for absolute beginners and teaches all the concepts you need — variables, input(), if/elif/else, functions, loops, and dictionaries — as you build the game.
The input() function pauses the program and waits for the user to type something and press Enter. It always returns the entered text as a string. You can pass a prompt string to input() that is displayed to the user before they type.
if evaluates a condition and runs its block only when the condition is True. elif (short for else if) checks an additional condition when the previous if was False. else runs its block when none of the preceding if or elif conditions matched.
A dictionary groups related data together under a single name. Instead of separate variables for each room's description, exits, and items, you can store all of it in one dictionary keyed by room name. This makes the code easier to read, extend, and loop over.
A while loop keeps running its body as long as a condition stays True. In a text adventure, a while True loop runs the game indefinitely — showing the current room, accepting input, and updating game state — until the player wins, dies, or types a quit command that triggers a break.
Add a new key-value pair to the rooms dictionary for each new room. Give it a description, an exits dictionary mapping direction strings to room names, and an optional items list. Then make sure an existing room has a matching direction that points to the new room's key.
.strip() removes any leading or trailing whitespace from the string, including newlines. .lower() converts all characters to lowercase. Chaining them together normalises user input so that "North", "north", and " NORTH " all match the same condition in your if statement.
Yes. The code in this tutorial uses only Python's standard library with no third-party packages, so it runs on Windows, macOS, and Linux. You need Python 3.6 or later installed. Run the script from a terminal with: python adventure.py