Building an AI Game Master with Temporal
A Journey into Game Design and Workflow Orchestration
Introduction
How many times has this happened to you: you have a bunch of friends that all want to play Dungeons and Dragons. It has been a while since your last campaign that ended in a fiery match against dragons and now you all want to start something new. But your Dungeon Master is busy this week. And next. And the next. No one else is up to the task either. So what happens?
No D&D campaign.
Now-a-days, we have AI! The magical invention that can solve this problem. Right?
Right?
Sad to say, that hasn't been my experience. So i decided to build my own Artificial Intelligent Game Master—AI-GM for short.
And, surprisingly, I have learned a lot more about Game Design than I am learning about Temporal.
What is 'ai-gm'?
My program is an AI-powered, Temporal-backed, game master system. It is built to:
- Manage complex game state (Player-Characters, Resources, Status, etc.)
- Handle turn-based gameplay and action resolution
- Maintain narrative consistency across sessions
- Provide asynchronous interaction using Temporal
It really is a fully-working Game Master: the Journal, the rulebook, the character sheets, and all!
Specifically, this one is built around 'Blades in the Dark'. I chose this system as my guineapig as it is more narrative-driven and less "mathy".
Why Temporal for a Game Master?
When I first heard about Temporal, I instantly knew I had the tools to build this project.
1. Natural State Persistence
TTRPG's, and ANY game, need to remember everything. Temporal automatically handles state durability through its event sourcing architecture. If a workflow crashes, Temporal replays the exact same actions to resume from where it left off. No lost progress, no starting from scratch.
2. Activity-Based Resolution
In games, actions have consequences. How a player acts affects the world. Even more so in a game like 'Blades'. Temporal runs on Activities—a small action with a determinable outcome. They execute, and Temporal "saves" the response.
3. Separation of Concerns
Temporal Forces clean separation:
- Workflows: The Order. These are kind of the Rules. They define the direction and loop the game works in.
- Activities: As stated before, they are the small actions a player can take. They make up a workflow.
- Workers: Execute workflows. They act out the Activities in the order defined by the Workflows.
- Starters: These are the entry points. The actual 'Start' buttons. They kick off the workers to get the games started.
This mirrors game architecture pretty well.
Okay. But what have you built so far?
Mostly, I have been messing around with the structure. No, not the folders and files (although there has been a lot of rearrangements of those too). I have spent time building the architecture: Players, Crews, Actions, Harm, Clocks, Playbooks. So much goes into the game than I thought!
I soon figured out that I can't just make a class like:
public class Player() {
private String name;
private int level;
private List<String> items;
//Getters and Setters
}
Instead, my Player class looks a littler bit more like this:
public class Player implements Serializable{
private String name;
private Truama truama;
private Map<Action, Integer> actionRatings;
private Harm harm;
private int coin;
private int stash;
private Playbook playbook;
public Player() { }
public Player(String name) {
this.name = name;
this.truama = new Truama();
// Initialize action ratings to 0
this.actionRatings = new HashMap<>();
for (Action action : Action.values()) {
actionRatings.put(action, 0);
}
this.harm = new Harm();
this.coin = 0;
this.stash = 0;
}
public Player(String name, Playbook playbook) {
this.name = name;
this.truama = new Truama();
// Initialize action ratings based on playbook starting actions
this.actionRatings = new HashMap<>();
for (Action action : Action.values()) {
actionRatings.put(action, 0);
}
for (Action action : playbook.getStartingActions()) {
actionRatings.put(action, 1); // Starting actions get a rating of 1
}
this.harm = new Harm();
this.coin = 0;
this.stash = 0;
this.playbook = playbook;
//Getters and Setters and Updaters and Tickers and...
}
And my Crew file is worse!
public class Crew implements Serializable{
private String name;
private CrewType type;
private List<Player> members;
private Heat heat;
private CrewStanding crewStanding;
private Clock crewXP;
private List<CrewAbility> abilities;
private List<Upgrade> upgrades;
private List<Contact> contacts;
private List<LairFeature> lair;
private List<FactionStatus> factionStatuses;
private List<Score> scores;
private List<Clock> clocks;
//And even more methods besides Getters and Setters
}
All of those custom Objects...
That's the nice part about games. We, as the players, see beautifully rendered pixels and mobs running at us just a dawn is about to break over the flattened mountains. The code underneath?? That's something I am now learning more about.
What I currently have is a bunch of Objects that represent parts of a "Blades in the Dark" campaign and my Temporal files. I know I have a LOT more to build for the actual game part of my "ai-gm", but getting the foundation out of the way was the first mountain I knew I had to climb.
What have I learned?
A few things about why Temporal is SO important in todays business world. A constant save for your AI workflows? Ummm... HELL YEAH!
But, my biggest lesson is how important mechanics are to games. Take one small mechanic from "Blades". Just one.
Okay, I'll choose. Clocks!
A clock is simple: A circle that is cut into pieces that get filled in after things occur. So, two intprimatives should work, the progress and the max, except that doesn't account for what clocks can do. Clocks are used for:
- healing
- escaping
- crafting
- war
- winning
- locating
- running
- losing
- hiding just to name a few things.
They can interact one with another: as one increases, another could decrease. And any thing could have any amount of clocks needed in the game. A player has a built in clock for healing, but they could also have another clock for their escape and their bleeding-out and their new gun and their new ritual. Two numbers isn't going to cut it anymore.
Mechanics are what a game is built on. They show what is important, how important they are, and are needed to do the simplest of things. Without them, we would be rolling dice for no reason.
Finally
Building "ai-gm" has been a fascinating exercise in both game design and distributed systems. Temporal's model beautifully fits gaming architecture, providing the durability and a natural way to think about a games statefulness.
If you are interested at all in workflows with AI, Game Design, or anything touching these topics, I'd tell you to check out Temporal. Experiment with it, find out what tools it has to help you with your projects. Anything you learn there can be brought far beyond games alone.
To check out the full source code (so far), check my GitHub!