Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
435: Building a Simple Text-Based Adventure Game
A few years ago, I was reviewing a PR for a junior dev who was building a small RPG for a game jam. He had written this massive, 400-line block of nested if and elif statements to handle player movement. It looked like a pyramid of doom. When he tried to add a sixth room to his map, he accidentally broke the logic for the first room, and suddenly players were teleporting from the dungeon straight to the credits screen. He spent an entire Saturday chasing a bug that was simply a misplaced indentation. That's when I sat him down and showed him that a game world isn't a sequence of logic gates—it's a data structure.
Mapping Your World with Dictionaries
If you try to hard-code every possible movement in a text adventure, you'll hit a wall very quickly. The trick is to separate your game logic from your game data. I prefer using a nested dictionary to represent the world. In this setup, each key is a room, and its value is another dictionary containing the room's description and the directions available to move to other rooms.
world_map = {
'Great Hall': {
'description': 'A vast room with flickering torches and a cold stone floor.',
'exits': {'north': 'Library', 'east': 'Kitchen'}
},
'Library': {
'description': 'Dusty shelves reach the ceiling. It smells of old parchment.',
'exits': {'south': 'Great Hall'}
},
'Kitchen': {
'description': 'A greasy hearth and a heavy iron pot. Something is simmering.',
'exits': {'west': 'Great Hall'}
}
}
By structuring the map this way, your movement code doesn't need to know which room the player is in. It just needs to check if the user's input exists as a key in the current room's exits dictionary. This makes your game infinitely expandable without adding a single new if statement to your movement logic.
Driving the Action with a Game Loop
Every game, from Pong to Elden Ring, runs on a loop. In a text adventure, this loop is quite simple: get input, update the state, and print the result. I usually wrap this in a while True loop that only breaks when a specific "win" or "lose" condition is met. You'll want to track the player's current location in a variable—let's call it current_room—and update it based on the movement logic we discussed.
current_room = 'Great Hall'
while True:
room = world_map[current_room]
print(f"\nYou are in the {current_room}.\n{room['description']}")
move = input("Which direction do you want to go? (north, south, east, west): ").lower()
if move in room['exits']:
current_room = room['exits'][move]
else:
print("You can't go that way!")
Notice how clean this is. The logic remains the same whether you have three rooms or three thousand. I've seen developers try to create separate functions for every room, but that's a recipe for a stack overflow and a lot of redundant code. Keep the loop lean.
Handling Inventory and Game State
A game isn't much of a game if you can't pick things up or unlock doors. To handle this, you can add an items list to your room dictionaries and a separate inventory list for the player. When a player types "get key", you check if the key is in the current room's list. If it is, you pop() it from the room and append() it to the player's inventory.
The real power comes when you tie movement to these items. Instead of just checking if a direction exists in exits, you can add a "locked" attribute to certain paths. Before updating current_room, check if the path is locked and if the player possesses the required key in their inventory list. It's a simple boolean check that transforms a walking simulator into an actual puzzle game.
📋 Practical Task
Building the Locked Cellar Gate
Your task is to expand the basic game loop into a small playable sequence. You need to create a world map with three rooms: a 'Hallway', a 'Cellar', and a 'Treasury'.
- The Hallway should have an exit to the Cellar (south).
- The Cellar should have an item called 'Rusty Key' and an exit to the Treasury (east).
- The Treasury should be the win condition; once the player enters this room, the game prints "You found the gold!" and exits the loop.
- The Twist: The exit from the Cellar to the Treasury must be locked. The player cannot enter the Treasury unless 'Rusty Key' is in their
inventorylist.
Implement a simple command system where the user can type "go [direction]" to move or "get [item]" to pick up the key.
There are no comments for now.