Castellan
A browser-based multiplayer game used to work through real-time state, turn flow, and disconnect handling.
I built Castellan as a personal project because multiplayer games expose problems that ordinary CRUD apps avoid: shared state, WebSocket updates, server-side validation, bot-filled seats, and sessions that need to survive unreliable browsers.
What the project does
Castellan runs as a browser game with four seats in a room. A player can start a room, play through turns, and see the match update through WebSocket messages. The interesting part is not the theme of the game. It is the server-side flow behind it: validating actions, keeping clients in sync, and deciding what happens when a player leaves.
How actions move through the server
The browser sends an action over WebSocket. The FastAPI backend receives it, checks it against the current room state, and sends the updated state back to the room. The client does not try to resolve the game locally; it renders what the backend sends.
This keeps the game state server-side and makes the browser responsible for presentation, not game truth.
The state machine keeps the game flow in one place.
The game has phases such as waiting, preparation, declaration, turns, scoring, and game over. I used a backend state machine so those phases and transitions are handled in one place instead of being spread across separate handlers.
The same update path also stores the event and broadcasts the new state. That made the code easier to reason about because a state change and a room update are not treated as two unrelated steps.
What happens when someone disconnects
Real-time browser apps have ordinary failure cases: a tab closes, a network drops, or someone leaves during their turn. Castellan handles that as part of the game flow instead of assuming every player stays connected.
When a player disconnects, the server gives them a short window to reconnect. If they do not return, a bot takes over that seat so the remaining players can keep playing.
Bots are part of the fallback path
The bots are rule-based. They are not machine learning, and they do not use an LLM. They exist to fill seats and to let a match continue when a human player is not available.
Bot moves go through the same action path as human moves. Once a bot submits an action, the state machine validates it, updates the room, and broadcasts the result like any other move.
Stack
The stack is intentionally direct: a React browser client, a FastAPI WebSocket backend, Docker runtime, and a state-machine architecture for game flow.
Live demo
Open the project and play through a room in the browser.