Welcome, aspiring web developers and game enthusiasts! Have you ever wanted to create your own web application but felt overwhelmed by complex frameworks? Today, we’re going to dive into the wonderful world of Flask, a lightweight Python web framework, and build a classic game: Hangman!
This project is perfect for beginners because it introduces core web development concepts like routing, templates, and session management in a fun and interactive way. By the end of this guide, you’ll have a fully functional web-based Hangman game running right in your browser. Let’s get started and have some fun with Python and Flask!
What is Flask? (A Quick Introduction)
Before we start coding, let’s briefly understand what Flask is.
- Flask: Imagine Flask as a small, easy-to-use toolkit for building websites and web applications using Python. It’s often called a “microframework” because it doesn’t try to do everything for you. Instead, it provides the essential tools, and you can add other components as needed. This makes it perfect for simpler projects or for learning the basics without getting bogged down by too many features.
- Web Framework: A web framework is a collection of libraries and modules that allows developers to create web applications easily without having to handle low-level details like protocols, sockets, or thread management. It gives you a structure to build your website upon.
Prerequisites
To follow along with this tutorial, you’ll need a few things:
- Python: Make sure you have Python installed on your computer (version 3.6 or newer is recommended). You can download it from the official Python website.
- Pip: This is Python’s package installer, and it usually comes bundled with Python. We’ll use it to install Flask.
- A Text Editor: Any code editor like VS Code, Sublime Text, or even Notepad will work.
Setting Up Your Environment
First, let’s create a dedicated space for our project to keep things organized.
-
Create a Project Folder:
Make a new folder for your game, for example,flask_hangman.
bash
mkdir flask_hangman
cd flask_hangman -
Create a Virtual Environment:
It’s good practice to use a virtual environment for each Python project. This keeps your project’s dependencies separate from other Python projects and your system’s global Python installation.- Virtual Environment (
venv): Think of it as a secluded little box where you can install Python libraries specifically for your current project, without affecting other projects on your computer.
To create one:
bash
python -m venv venv
This command creates a folder namedvenvinside your project folder. - Virtual Environment (
-
Activate the Virtual Environment:
- On Windows:
bash
venv\Scripts\activate - On macOS/Linux:
bash
source venv/bin/activate
You’ll notice(venv)appear at the beginning of your command prompt, indicating that the virtual environment is active.
- On Windows:
-
Install Flask:
Now that your virtual environment is active, install Flask usingpip.- Pip: A command-line tool that lets you install and manage Python software packages.
bash
pip install Flask
Flask and its necessary components will be installed within your virtual environment.
Understanding the Hangman Game Logic
Before we write code, let’s break down how Hangman works:
- Secret Word: The game starts with a hidden word.
- Guesses: The player guesses letters one by one.
- Correct Guess: If the letter is in the word, it’s revealed in all its positions.
- Incorrect Guess: If the letter is not in the word, the player loses a “life” (or a part of the hangman figure is drawn).
- Win Condition: The player wins if they guess all letters in the word before running out of lives.
- Lose Condition: The player loses if they run out of lives before guessing the word.
- Previously Guessed Letters: Players shouldn’t be able to guess the same letter multiple times.
For our web game, we’ll need to store the game’s state (secret word, guessed letters, remaining lives) as the user makes multiple requests to the server. Flask’s session feature is perfect for this!
- Session: In web development, a session is a way for a web server to remember information about a specific user over multiple requests. Since web pages are “stateless” (they don’t remember what happened before), sessions help maintain continuity, like remembering a user’s logged-in status or, in our case, the current game’s progress.
Building the Flask Application (app.py)
Create a file named app.py in your flask_hangman folder. This will contain all our Python code for the game logic.
1. Initial Setup and Imports
from flask import Flask, render_template, request, redirect, url_for, session
import random
app = Flask(__name__)
app.secret_key = 'your_secret_key_here' # IMPORTANT: Change this for production!
WORDS = [
"python", "flask", "hangman", "programming", "developer",
"challenge", "computer", "internet", "website", "application"
]
from flask import ...: Imports necessary tools from Flask.Flask: The main class to create your application.render_template: Used to display HTML files.request: Allows us to access incoming request data (like form submissions).redirect,url_for: Used to send the user to a different page.session: For storing game state across requests.
import random: We’ll use this to pick a random word from our list.app = Flask(__name__): Initializes our Flask application.app.secret_key = ...: Crucial for sessions! Flask uses this key to securely sign session cookies. Without it, sessions won’t work, or they’ll be insecure. Remember to change'your_secret_key_here'to a long, random string for any real-world application!WORDS: Our list of potential secret words.
2. Helper Functions
Let’s create a few helper functions to manage the game logic.
def get_masked_word(word, guessed_letters):
masked = ""
for letter in word:
if letter in guessed_letters:
masked += letter
else:
masked += "_"
return masked
def is_game_won(word, guessed_letters):
return all(letter in guessed_letters for letter in word)
def is_game_lost(lives):
return lives <= 0
get_masked_word: Takes the secret word and the letters guessed so far. It returns a string likep_th_nif ‘p’, ‘t’, ‘h’, ‘n’ have been guessed for “python”.is_game_won: Checks if every letter in the secret word has been guessed.is_game_lost: Checks if the player has run out of lives.
3. Routes (Handling Web Pages)
Flask uses “routes” to map URLs to Python functions.
- Route: A route defines what happens when a user visits a specific URL on your website. For example, the
/route usually refers to the homepage.
The Homepage (/)
@app.route('/')
def index():
# If starting a new game or no game in session, initialize game state
if 'secret_word' not in session or request.args.get('new_game'):
session['secret_word'] = random.choice(WORDS).lower()
session['guessed_letters'] = []
session['lives'] = 6 # Standard Hangman starts with 6-7 lives
session['message'] = "Guess a letter!"
secret_word = session['secret_word']
guessed_letters = session['guessed_letters']
lives = session['lives']
message = session['message']
masked_word = get_masked_word(secret_word, guessed_letters)
# Check for win/loss conditions to display appropriate messages
if is_game_won(secret_word, guessed_letters):
message = f"Congratulations! You guessed the word: '{secret_word}'"
elif is_game_lost(lives):
message = f"Game Over! The word was '{secret_word}'."
return render_template('index.html',
masked_word=masked_word,
guessed_letters=sorted(guessed_letters), # Display sorted for readability
lives=lives,
message=message,
game_over=is_game_won(secret_word, guessed_letters) or is_game_lost(lives))
@app.route('/'): This decorator tells Flask that theindexfunction should run when someone visits the root URL (/) of our application.session['secret_word'] = ...: We store the chosen word in the user’s session.request.args.get('new_game'): Checks if the URL contains?new_game=True, which would signal a request to start over.render_template('index.html', ...): This function looks for an HTML file namedindex.htmlin a folder calledtemplates(which we’ll create next) and sends it to the user’s browser. We pass variables (likemasked_word,lives) to the template so they can be displayed.
The Guess Endpoint (/guess)
@app.route('/guess', methods=['POST'])
def guess():
secret_word = session.get('secret_word')
guessed_letters = session.get('guessed_letters', [])
lives = session.get('lives', 6)
# Prevent further guesses if the game is already over
if is_game_won(secret_word, guessed_letters) or is_game_lost(lives):
return redirect(url_for('index'))
guess_letter = request.form['letter'].lower()
session['message'] = "" # Clear previous messages
if len(guess_letter) != 1 or not guess_letter.isalpha():
session['message'] = "Please enter a single letter."
elif guess_letter in guessed_letters:
session['message'] = f"You already guessed '{guess_letter}'. Try another letter!"
else:
guessed_letters.append(guess_letter)
session['guessed_letters'] = guessed_letters # Update session
if guess_letter not in secret_word:
lives -= 1
session['lives'] = lives
session['message'] = f"'{guess_letter}' is not in the word. You lost a life!"
else:
session['message'] = f"Good guess! '{guess_letter}' is in the word."
# Redirect back to the index page to display updated game state
return redirect(url_for('index'))
@app.route('/guess', methods=['POST']): This route only acceptsPOSTrequests, which is standard for form submissions.request.form['letter']: This accesses the data sent from the HTML form (specifically the input field named ‘letter’).- Game Logic Updates: We check if the guess is valid, if it’s already guessed, if it’s in the word, and update
guessed_letters,lives, andmessagein thesessionaccordingly. redirect(url_for('index')): After processing the guess, we redirect the user back to the homepage (/). This is a common pattern called “Post/Redirect/Get” which prevents duplicate form submissions if the user refreshes the page.
4. Running the App
if __name__ == '__main__':
app.run(debug=True)
if __name__ == '__main__':: This standard Python construct ensures thatapp.run()is called only when you runapp.pydirectly (not when imported as a module).app.run(debug=True): Starts the Flask development server.debug=Truemeans that the server will automatically reload when you make changes to your code, and it will show you detailed error messages in the browser. Always setdebug=Falsein production environments for security.
Creating the HTML Template (templates/index.html)
Flask expects your HTML templates to be in a folder named templates within your project directory. So, create a new folder called templates inside flask_hangman, and then create a file named index.html inside templates.
Your project structure should now look like this:
flask_hangman/
├── venv/
├── app.py
└── templates/
└── index.html
Now, open templates/index.html and add the following HTML code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask Hangman Game</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
color: #333;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
}
.container {
background-color: #fff;
padding: 30px 50px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
text-align: center;
max-width: 500px;
width: 90%;
}
h1 {
color: #0056b3;
margin-bottom: 20px;
}
.word-display {
font-size: 3em;
letter-spacing: 5px;
margin: 30px 0;
font-weight: bold;
color: #28a745;
}
.message {
margin-top: 20px;
font-size: 1.2em;
color: #dc3545; /* For error/game over messages */
}
.message.success {
color: #28a745; /* For success messages */
}
.message.info {
color: #007bff; /* For general info */
}
.guessed-letters {
margin: 20px 0;
font-size: 1.1em;
}
.lives {
font-size: 1.1em;
color: #ffc107;
font-weight: bold;
}
form {
margin-top: 30px;
display: flex;
justify-content: center;
align-items: center;
}
input[type="text"] {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
width: 60px;
text-align: center;
font-size: 1.2em;
margin-right: 10px;
text-transform: lowercase;
}
button {
background-color: #007bff;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1.1em;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #0056b3;
}
.new-game-button {
background-color: #6c757d;
margin-top: 20px;
padding: 10px 20px;
border-radius: 4px;
color: white;
text-decoration: none;
display: inline-block;
font-size: 1.1em;
transition: background-color 0.3s ease;
}
.new-game-button:hover {
background-color: #5a6268;
}
</style>
</head>
<body>
<div class="container">
<h1>Hangman Game</h1>
<p class="message {% if 'Good guess' in message %}success{% elif 'already guessed' in message or 'not in the word' in message %}danger{% else %}info{% endif %}">
{{ message }}
</p>
<div class="word-display">
{{ masked_word }}
</div>
<p class="lives">
Lives Left: {{ lives }}
</p>
<p class="guessed-letters">
Guessed Letters: {{ guessed_letters | join(', ') }}
</p>
{% if not game_over %}
<form action="{{ url_for('guess') }}" method="post">
<label for="letter">Guess a letter:</label>
<input type="text" id="letter" name="letter" maxlength="1" required autocomplete="off">
<button type="submit">Guess</button>
</form>
{% else %}
<a href="{{ url_for('index', new_game='true') }}" class="new-game-button">Start New Game</a>
{% endif %}
</div>
</body>
</html>
- Jinja Templating: Flask uses a templating engine called Jinja2. This allows you to embed Python-like logic directly into your HTML.
{{ variable_name }}: Used to display the value of a variable passed from your Flask application.{% if condition %}/{% else %}/{% endif %}: Used for conditional logic.{{ list_variable | join(', ') }}: A Jinja filter that joins items in a list with a comma and space.
masked_word: Displays the word with underscores for unguessed letters.lives: Shows how many lives are left.guessed_letters: Lists all the letters the user has tried.<form action="{{ url_for('guess') }}" method="post">: This form sends the user’s guess to our/guessroute using thePOSTmethod.url_for('guess')dynamically generates the URL for theguessfunction.input type="text" id="letter" name="letter" maxlength="1" required: An input field where the user types their guess.name="letter"is important because Flask’srequest.form['letter']uses this name to get the value.maxlength="1"ensures only one character can be entered.{% if not game_over %}: This block ensures the guess form is only visible if the game is still active. If the game is over, a “Start New Game” button appears instead.<a href="{{ url_for('index', new_game='true') }}" ...>: This link will restart the game by sending anew_game=trueparameter to theindexroute.
Running Your Hangman Game!
You’re all set! Now, let’s run your Flask application.
- Ensure your virtual environment is active. (If you closed your terminal,
cd flask_hangmanand reactivate it using the commands from “Setting Up Your Environment” section). - Navigate to your
flask_hangmandirectory in the terminal. - Set the
FLASK_APPenvironment variable:- On Windows:
bash
set FLASK_APP=app.py - On macOS/Linux:
bash
export FLASK_APP=app.py FLASK_APP: This environment variable tells Flask where to find your application file.
- On Windows:
- Run the Flask application:
bash
flask run
You should see output similar to this:
* 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: ...
Open your web browser and go to http://127.0.0.1:5000. You should now see your Hangman game! Try guessing letters, win, lose, and start new games.
Conclusion
Congratulations! You’ve successfully built a simple but fully functional Hangman game using Python and Flask. You’ve learned about:
- Setting up a Flask project with a virtual environment.
- Defining routes to handle different web pages.
- Using Flask’s
sessionto manage game state across requests. - Rendering HTML templates with Jinja2 to display dynamic content.
- Handling form submissions (
POSTrequests).
This project is a great foundation. From here, you could enhance it by:
- Adding CSS to make it look much prettier (we included a basic style block, but you can move it to a separate
static/style.cssfile!). - Creating a proper graphical representation of the hangman figure.
- Adding more words or allowing users to input their own.
- Implementing user accounts and high scores.
Keep experimenting, keep building, and happy coding!