Web Games with FastAPI
Games come in various forms, from text-based adventures to immersive 3D experiences. In this article, we’ll explore the creation of a simple game using FastAPI, a modern web framework for building APIs with Python.
Our focus will be on developing a Wordle-like game, incorporating both client-side and server-side interactions.
Python Game Packages:
Before diving into game development, it’s worth noting some Python game packages for those interested in more complex gaming projects. However, our primary goal is to keep the game code simple and concise, aligning with the theme of creating web content with FastAPI.
Splitting Game Logic:
Creating a game involves deciding where to store and manage game logic. The web is stateless, so maintaining state across game steps is crucial. We explore different approaches, from client-side JavaScript to server-heavy applications and single-page applications (SPAs).
Game Design:
Our game will be a simplified Wordle-style challenge, using creature names from a database. The design involves initializing the game, processing user guesses, scoring them, and providing visual feedback on the client side.
Web Part One: Game Initialization:
We introduce two new FastAPI web endpoints under the namespace “/game”. The first endpoint initializes the game, retrieving a random creature name from the database. The response includes a Jinja template containing HTML, CSS, and JavaScript to implement the game logic.
Python Code:
# web/game.py
from pathlib import Path
from fastapi import APIRouter, Body, Request
from fastapi.templating import Jinja2Templates
from service import game as service
router = APIRouter(prefix=”/game”)
# Initial game request
@router.get(“”)
def game_start(request: Request):
name = service.get_word()
top = Path(__file__).resolve().parents[1]
templates = Jinja2Templates(directory=f”{top}”)
return templates.TemplateResponse(“game.html”, {“request”: request, “word”: name})
# Subsequent game requests
@router.post(“”)
async def game_step(word: str = Body(), guess: str = Body()):
score = service.get_score(word, guess)
return score
Web Part Two: Game Steps:
The client-side template is crucial for game interaction. It includes HTML, CSS, and JavaScript to display the game interface, handle user input, and communicate with the server using the fetch() function. The template also manages the scoring and visual feedback for user guesses.
HTML Code
<!– template/game.html –>
<head>
<!– Styles and head content as mentioned in the example –>
</head>
<body>
<!– JavaScript and HTML content as mentioned in the example –>
</body>
Service Part One: Initialization:
The service layer connects the web layer’s game initialization function to the data layer, ensuring a random creature name is provided to start the game.
Python Code:
# service/game.py
import data.game as data
def get_word() -> str:
return data.get_word()
Service Part Two: Scoring:
The scoring logic is added to the service layer, determining whether a guessed letter is a hit, close, or miss. The results are sent back to the client for visual representation.
Python Code:
# service/game.py
from collections import Counter, defaultdict
HIT = “H”
MISS = “M”
CLOSE = “C”
def get_score(actual: str, guess: str) -> str:
length: int = len(actual)
if len(guess) != length:
return “”
actual_counter = Counter(actual)
guess_counter = defaultdict(int)
result = [MISS] * length
for pos, letter in enumerate(guess):
if letter == actual[pos]:
result[pos] = HIT
guess_counter[letter] += 1
for pos, letter in enumerate(guess):
if result[pos] == HIT:
continue
guess_counter[letter] += 1
if (letter in actual and
guess_counter[letter] <= actual_counter[letter]):
result[pos] = CLOSE
result = ”.join(result)
return result
Test:
We include pytest exercises to test the service layer’s score calculation, ensuring the correctness of the game logic.
Python Code:
# test/unit/service/test_game.py
import pytest
from service import game
word = “bigfoot”
guesses = [
(“bigfoot”, “HHHHHHH”),
(“abcdefg”, “MCMMMCC”),
(“toofgib”, “CCCHCCC”),
(“wronglength”, “”),
(“”, “”),
]
@pytest.mark.parametrize(“guess, score”, guesses)
def test_match(guess, score):
assert game.get_score(word, guess) == score
Data: Initialization:
The data layer provides a function to retrieve a random creature name from the database.
Python Code:
# data/game.py
from .init import curs
def get_word() -> str:
qry = “select name from creature order by random() limit 1”
curs.execute(qry)
row = curs.fetchone()
if row:
name = row[0]
else:
name = “bigfoot”
return name
Let’s Play Cryptonomicon:
A demonstration of the game is provided, showcasing the interactive nature of the Wordle-style challenge. Users can input guesses, receive scores, and continue playing until they guess the creature name or give up.
This article guides you through the process of creating an interactive web game using FastAPI and Python. We’ve covered aspects of game design, web development, and backend logic. The provided code examples serve as a foundation for building more complex games and exploring the world of web-based gaming with Python.