Have you ever dreamed of creating your own interactive story, where players make choices that shape their destiny? Text adventure games are a fantastic way to do just that! They’re like digital “Choose Your Own Adventure” books, where you read a description and then decide what to do next.
In this guide, we’re going to build a simple text adventure game using Flask, a popular and easy-to-use tool for making websites with Python. Don’t worry if you’re new to web development or Flask; we’ll take it step by step, explaining everything along the way. Get ready to dive into the world of web development and game creation!
What is a Text Adventure Game?
Imagine a game where there are no fancy graphics, just words describing your surroundings and situations. You type commands or click on choices to interact with the world. For example, the game might say, “You are in a dark forest. A path leads north, and a faint light flickers to the east.” You then choose “Go North” or “Go East.” The game responds with a new description, and your adventure continues!
Why Flask for Our Game?
Flask (pronounced “flask”) is what we call a micro web framework for Python.
* Web Framework: Think of it as a set of tools and rules that help you build web applications (like websites) much faster and easier than starting from scratch.
* Micro: This means Flask is lightweight and doesn’t force you into specific ways of doing things. It’s flexible, which is great for beginners and for projects like our game!
We’ll use Flask because it allows us to create simple web pages that change based on player choices. Each “room” or “situation” in our game will be a different web page, and Flask will help us manage how players move between them.
Prerequisites: What You’ll Need
Before we start coding, make sure you have these things ready:
- Python: The programming language itself. You should have Python 3 installed on your computer. You can download it from python.org.
- Basic Python Knowledge: Understanding variables, dictionaries, and functions will be helpful, but we’ll explain the specific parts we use.
pip: This is Python’s package installer, which usually comes installed with Python. We’ll use it to install Flask.
Setting Up Our Flask Project
First, let’s create a dedicated folder for our game and set up our development environment.
1. Create a Project Folder
Make a new folder on your computer named text_adventure_game.
mkdir text_adventure_game
cd text_adventure_game
2. Create a Virtual Environment
It’s good practice to use a virtual environment for your Python projects.
* Virtual Environment: This creates an isolated space for your project’s Python packages. It prevents conflicts between different projects that might need different versions of the same package.
python3 -m venv venv
This command creates a new folder named venv inside your project folder. This venv folder contains a local Python installation just for this project.
3. Activate the Virtual Environment
You need to activate this environment to use it.
- On macOS/Linux:
bash
source venv/bin/activate - On Windows (Command Prompt):
bash
venv\Scripts\activate.bat - On Windows (PowerShell):
bash
venv\Scripts\Activate.ps1
You’ll know it’s active when you see (venv) at the beginning of your command line prompt.
4. Install Flask
Now, with your virtual environment active, install Flask:
pip install Flask
5. Create Our First Flask Application (app.py)
Create a new file named app.py inside your text_adventure_game folder. This will be the main file for our game.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_adventurer():
return '<h1>Hello, Adventurer! Welcome to your quest!</h1>'
if __name__ == '__main__':
# app.run() starts the Flask development server
# debug=True allows for automatic reloading on code changes and shows helpful error messages
app.run(debug=True)
Explanation:
* from flask import Flask: We import the Flask class from the flask library.
* app = Flask(__name__): This creates our Flask application. __name__ is a special Python variable that tells Flask the name of the current module, which it needs to locate resources.
* @app.route('/'): This is a “decorator.” It tells Flask that when someone visits the root URL (e.g., http://127.0.0.1:5000/), the hello_adventurer function should be called.
* def hello_adventurer():: This function is called when the / route is accessed. It simply returns an HTML string.
* if __name__ == '__main__':: This standard Python construct ensures that app.run(debug=True) is executed only when app.py is run directly (not when imported as a module).
* app.run(debug=True): This starts the Flask development server. debug=True is very useful during development as it automatically restarts the server when you make code changes and provides detailed error messages in your browser.
6. Run Your First Flask App
Go back to your terminal (with the virtual environment active) and run:
python app.py
You should see output similar to this:
* Serving Flask app 'app'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 234-567-890
Open your web browser and go to http://127.0.0.1:5000/. You should see “Hello, Adventurer! Welcome to your quest!”
Congratulations, your Flask app is running! Press CTRL+C in your terminal to stop the server for now.
Designing Our Adventure Game Logic
A text adventure game is essentially a collection of “rooms” or “scenes,” each with a description and a set of choices that lead to other rooms. We can represent this structure using a Python dictionary.
Defining Our Game Rooms
Let’s define our game world in a Python dictionary. Each key in the dictionary will be a unique room_id (like ‘start’, ‘forest_edge’), and its value will be another dictionary containing the description of the room and its choices.
Create this rooms dictionary either directly in app.py for simplicity or in a separate game_data.py file if you prefer. For this tutorial, we’ll put it directly into app.py.
rooms = {
'start': {
'description': "You are in a dimly lit cave. There's a faint path to the north and a dark hole to the south.",
'choices': {
'north': 'forest_edge', # Choice 'north' leads to 'forest_edge' room
'south': 'dark_hole' # Choice 'south' leads to 'dark_hole' room
}
},
'forest_edge': {
'description': "You emerge from the cave into a dense forest. A faint path leads east, and the cave entrance is behind you.",
'choices': {
'east': 'old_ruins',
'west': 'start' # Go back to the cave
}
},
'dark_hole': {
'description': "You bravely venture into the dark hole. It's a dead end! There's nothing but solid rock further in. You must turn back.",
'choices': {
'back': 'start' # No other options, must go back
}
},
'old_ruins': {
'description': "You discover ancient ruins, overgrown with vines. Sunlight filters through crumbling walls, illuminating a hidden treasure chest! You open it to find untold riches. Congratulations, Adventurer, you've won!",
'choices': {} # An empty dictionary means no more choices, game ends here for this path
}
}
Explanation of rooms dictionary:
* Each key (e.g., 'start', 'forest_edge') is a unique identifier for a room.
* Each value is another dictionary with:
* 'description': A string explaining what the player sees and experiences in this room.
* 'choices': Another dictionary. Its keys are the visible choice text (e.g., 'north', 'back'), and its values are the room_id where that choice leads.
* An empty choices dictionary {} signifies an end point in the game.
Building the Game Interface with Flask
Instead of returning raw HTML strings from our functions, Flask uses Jinja2 templates for creating dynamic web pages.
* Templates: These are HTML files with special placeholders and logic (like loops and conditions) that Flask fills in with data from our Python code. This keeps our Python code clean and our HTML well-structured.
1. Create a templates Folder
Flask automatically looks for templates in a folder named templates inside your project. Create this folder:
mkdir templates
2. Create the game.html Template
Inside the templates folder, create a new file named game.html:
<!-- templates/game.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Text Adventure Game</title>
<style>
body {
font-family: 'Georgia', serif;
max-width: 700px;
margin: 40px auto;
padding: 20px;
background-color: #f4f4f4;
color: #333;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
line-height: 1.6;
}
h1 {
color: #2c3e50;
text-align: center;
border-bottom: 2px solid #ccc;
padding-bottom: 10px;
margin-bottom: 30px;
}
p {
margin-bottom: 15px;
font-size: 1.1em;
}
.choices {
margin-top: 30px;
border-top: 1px solid #eee;
padding-top: 20px;
}
.choices p {
font-weight: bold;
font-size: 1.15em;
color: #555;
margin-bottom: 15px;
}
.choice-item {
display: block; /* Each choice on a new line */
margin-bottom: 10px;
}
.choice-item a {
text-decoration: none;
color: #007bff;
background-color: #e9f5ff;
padding: 10px 15px;
border-radius: 5px;
transition: background-color 0.3s ease, color 0.3s ease;
display: inline-block; /* Allows padding and background */
min-width: 120px; /* Ensure buttons are somewhat consistent */
text-align: center;
border: 1px solid #007bff;
}
.choice-item a:hover {
background-color: #007bff;
color: white;
text-decoration: none;
box-shadow: 0 2px 5px rgba(0, 123, 255, 0.3);
}
.end-game-message {
margin-top: 30px;
padding: 15px;
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
border-radius: 5px;
text-align: center;
}
.restart-link {
display: block;
margin-top: 20px;
text-align: center;
}
</style>
</head>
<body>
<h1>Your Text Adventure!</h1>
<p>{{ description }}</p>
{% if choices %} {# If there are choices, show them #}
<div class="choices">
<p>What do you do?</p>
{% for choice_text, next_room_id in choices.items() %} {# Loop through the choices #}
<span class="choice-item">
{# Create a link that goes to the 'play_game' route with the next room's ID #}
> <a href="{{ url_for('play_game', room_id=next_room_id) }}">{{ choice_text.capitalize() }}</a>
</span>
{% endfor %}
</div>
{% else %} {# If no choices, the game has ended #}
<div class="end-game-message">
<p>The adventure concludes here!</p>
<div class="restart-link">
<a href="{{ url_for('play_game', room_id='start') }}">Start A New Adventure!</a>
</div>
</div>
{% endif %}
</body>
</html>
Explanation of game.html (Jinja2 features):
* {{ description }}: This is a Jinja2 variable. Flask will replace this placeholder with the description value passed from our Python code.
* {% if choices %} … {% endif %}: This is a Jinja2 conditional statement. The content inside this block will only be displayed if the choices variable passed from Flask is not empty.
* {% for choice_text, next_room_id in choices.items() %} … {% endfor %}: This is a Jinja2 loop. It iterates over each item in the choices dictionary. For each choice, choice_text will be the key (e.g., “north”), and next_room_id will be its value (e.g., “forest_edge”).
* {{ url_for('play_game', room_id=next_room_id) }}: This is a powerful Flask function called url_for. It generates the correct URL for a given Flask function (play_game in our case), and we pass the room_id as an argument. This is better than hardcoding URLs because Flask handles changes if your routes ever change.
* A bit of CSS is included to make our game look nicer than plain text.
3. Updating app.py for Game Logic and Templates
Now, let’s modify app.py to use our rooms data and game.html template.
from flask import Flask, render_template, request # Import render_template and request
app = Flask(__name__)
rooms = {
'start': {
'description': "You are in a dimly lit cave. There's a faint path to the north and a dark hole to the south.",
'choices': {
'north': 'forest_edge',
'south': 'dark_hole'
}
},
'forest_edge': {
'description': "You emerge from the cave into a dense forest. A faint path leads east, and the cave entrance is behind you.",
'choices': {
'east': 'old_ruins',
'west': 'start'
}
},
'dark_hole': {
'description': "You bravely venture into the dark hole. It's a dead end! There's nothing but solid rock further in. You must turn back.",
'choices': {
'back': 'start'
}
},
'old_ruins': {
'description': "You discover ancient ruins, overgrown with vines. Sunlight filters through crumbling walls, illuminating a hidden treasure chest! You open it to find untold riches. Congratulations, Adventurer, you've won!",
'choices': {}
}
}
@app.route('/')
@app.route('/play/<room_id>') # This new route captures a variable part of the URL: <room_id>
def play_game(room_id='start'): # room_id will be 'start' by default if no <room_id> is in the URL
# Get the current room's data from our 'rooms' dictionary
# .get() is safer than direct access (rooms[room_id]) as it returns None if key not found
current_room = rooms.get(room_id)
# If the room_id is invalid (doesn't exist in our dictionary)
if not current_room:
# We'll redirect the player to the start of the game or show an error
return render_template(
'game.html',
description="You find yourself lost in the void. It seems you've wandered off the path! Try again.",
choices={'return to start': 'start'}
)
# Render the game.html template, passing the room's description and choices
return render_template(
'game.html',
description=current_room['description'],
choices=current_room['choices']
)
if __name__ == '__main__':
app.run(debug=True)
Explanation of updated app.py:
* from flask import Flask, render_template, request: We added render_template (to use our HTML templates) and request (though we don’t strictly use request object itself here, it’s often imported when dealing with routes that process user input).
* @app.route('/play/<room_id>'): This new decorator tells Flask to match URLs like /play/start, /play/forest_edge, etc. The <room_id> part is a variable part of the URL, which Flask will capture and pass as an argument to our play_game function.
* def play_game(room_id='start'):: The room_id parameter in the function signature will receive the value captured from the URL. We set a default of 'start' so that if someone just goes to / (which also maps to this function), they start at the beginning.
* current_room = rooms.get(room_id): We safely retrieve the room data. Using .get() is good practice because if room_id is somehow invalid (e.g., someone types a wrong URL), it returns None instead of causing an error.
* if not current_room:: This handles cases where an invalid room_id is provided in the URL, offering a way back to the start.
* return render_template(...): This is the core of displaying our game. We call render_template and tell it which HTML file to use ('game.html'). We also pass the description and choices from our current_room dictionary. These become the variables description and choices that Jinja2 uses in game.html.
Running Your Game!
Save both app.py and templates/game.html. Make sure your virtual environment is active in your terminal.
Then run:
python app.py
Open your web browser and navigate to http://127.0.0.1:5000/.
You should now see your text adventure game! Click on the choices to navigate through your story. Try to find the hidden treasure!
Next Steps & Enhancements
This is just the beginning! Here are some ideas to expand your game:
- More Complex Stories: Add more rooms, branches, and dead ends.
- Inventory System: Let players pick up items and use them. This would involve storing the player’s inventory, perhaps in Flask’s
sessionobject (which is a way to store data unique to each user’s browser session). - Puzzles: Introduce simple riddles or challenges that require specific items or choices to solve.
- Player Stats: Add health, score, or other attributes that change during the game.
- Multiple Endings: Create different win/lose conditions based on player choices.
- CSS Styling: Enhance the visual appearance of your game further.
- Better Error Handling: Provide more user-friendly messages for invalid choices or paths.
- Save/Load Game: Implement a way for players to save their progress and resume later. This would typically involve storing game state in a database.
Conclusion
You’ve just built a fully functional text adventure game using Python and Flask! You’ve learned about:
- Setting up a Flask project.
- Defining web routes and handling URL variables.
- Using Python dictionaries to structure game data.
- Creating dynamic web pages with Jinja2 templates.
- Passing data from Python to HTML templates.
This project is a fantastic stepping stone into web development and game design. Flask is incredibly versatile, and the concepts you’ve learned here apply to many other web applications. Keep experimenting, keep building, and most importantly, have fun creating your own interactive worlds!
Leave a Reply
You must be logged in to post a comment.