Skip to content
Eric®
Portfolio & Journal中文

Work / AI Agents · Strategy Game · Simulation

Civilization Z

I wanted to put AI decisions on a map where their consequences are visible. Civilization Z is my attempt: three civilizations set out from their capitals to explore, build and compete, while I can switch between watching and taking command.

Role
Game & AI application development
Status
Playable locally · In development
Stack
React / TypeScript / Fastify
Index
No. 01
Civilization Z main menu over an ancient city landscape, with spectating, custom world, challenge, tutorial and save options

Why I wanted to build an AI civilization sandbox

These are screenshots from the running game, using manually staged scenarios to show different parts of the experience. They are not an autonomous AI playthrough. The images retain the original Chinese UI; select one to view it at full size.

If several AIs developed on the same continent, how would they choose their paths? That is the question I most wanted to explore with Civilization Z. I gave three civilizations a default 24 × 18 hex map and a capital each, with fog to explore, cities to establish and resources to gather. They act in sequence before a shared settlement phase, with a default limit of 60 rounds.

What interests me is how those choices change the map: where a city goes, how resources are split between research and expansion, and when to prepare for conflict. I kept a free offline rule-based strategy so the game can run without an API; it does not call a language model. Connecting DeepSeek → Jev lets me observe model decisions under the same rules. For now, it is a single-player sandbox running in a local browser.

TypeScriptReact 19Canvas 2DFastifyJSON Schema / AJVDeepSeekJev
Civilization Z hex map showing three civilizations, cities, borders and units in a manually staged scenario
One shared continent: mountain passes, mineral belts and borders shape where the three civilizations can grow.

Making city development a choice

I wanted a city to need attention after it was founded. Population, food and production set its pace; granaries, workshops, libraries and new units take multiple rounds. Build another settler first, or invest in a library? Limited production time gives that choice weight.

Research, industry and military specializations give cities different roles, alongside limited construction investment. I also kept direct controls: during a takeover, players can select a city to arrange production or a unit to set a destination and inspect attack risks. Natural language is useful for expressing intent, but it should not be required for every move.

Civilization Z city drawer showing population, yields, workshop production and city specialization
Building a city: population, production, construction and specialization give each settlement a role.

Leaving room for different paths

Victory conditions give development a purpose. I set up two ways to finish early: a science victory requires Scientific Method followed by its research project; conquest requires control of every initial capital at once. If neither happens before the round limit, the game is decided by a combined score.

I want these routes to pull against one another. Research depends on city output, expansion requires construction, and preparing for war uses resources too. Shared limits on time and resources let an immediate choice shape the later game. Giving each route a fair chance is still part of the balancing work ahead.

Civilization Z research drawer showing Scientific Method progress, science yield and completed technologies
The path of knowledge: technologies unlock over time, with research and city development shaping the route ahead.

Taking part without having to take over

When AI simply keeps playing, it can be hard to tell why it stopped or what it is working toward. I added map action bubbles, a location-aware chronicle and public goals to make spectating easier to follow. Intentions stay separate from results. Viewers can follow a civilization or region and pause automatically at events such as a declaration of war or an attack on a capital.

I split participation into two levels. A lighter intervention gives a civilization direction for five AI decisions, such as prioritizing research or defending territory, while leaving individual moves to it. For direct control, players can take over at its next eligible action phase. Leaving ends that phase, with AI returning in the following round.

Civilization Z strategy drawer showing declared research, settlement and unit-training goals for three civilizations
Three ambitions: declared goals sit side by side, ready to compare with what each civilization does next.

Letting the map tell the story

I would rather a viewer remember a fight at a mountain pass or a city changing hands than another few lines in a log. Units move under terrain, movement and occupancy rules; the engine resolves attacks and counterattacks. The interface then turns those events into paths, damage numbers and bubbles. Even a long march has visible progress on the map.

I chose miniature cities, hand-painted terrain and an antique-map palette, with city art changing as population grows. Drawers open only when needed, leaving most of the screen to the world. Resource and military overlays, labels and text sizing are adjustable. Rather than display everything at once, I want the current situation to be readable at a glance.

Civilization Z units fighting near a mountain pass, with an attack bubble and damage numbers for both sides
Clash at the pass: the location, losses on both sides and action feedback appear directly on the map.

Connecting model decisions to a rule engine

The engineering separation I care most about is between a model expressing intent and the game changing state. TypeScript workspaces hold the rule engine, AI adapter, shared contracts, Fastify server and React frontend. Model decisions follow the four stages below. The offline strategy produces candidate commands directly, but uses the same rule validation.

  1. 01 / OBSERVE

    Scoped observation

    Each civilization receives its own state, visible map and private memory. Spectators can see the whole world; a model decides from its civilization's perspective.

  2. 02 / TRANSLATE

    Strategy & translation

    DeepSeek proposes actions and Jev translates them into structured commands tied to source text spans. Plans, quotations and unresolved text are not executed directly.

  3. 03 / VALIDATE

    Server adjudication

    The server checks the run, state version and control authority. The rule engine then validates commands and resolves movement, construction and combat.

  4. 04 / RECORD

    Facts & feedback

    Execution results become events that drive the map, chronicle and next observation. Blocked actions retain their reasons for explanation and debugging.

Human natural-language commands also go through preview, confirmation and execution. Loading a save creates a new runtime instance so old requests cannot change the restored world. Model budgets are tracked separately, and loading an earlier save does not reset consumed calls. These are quiet details, but they matter when pause, takeover and saving need to work together reliably.

I kept pause, single-step and speed controls in the viewing experience. Sometimes I just want to watch development; sometimes I want to stop and understand one decision. Saves let a game continue, while the chronicle recalls key events. A full replay of model responses is not implemented yet; that is a different problem from restoring a game.

Next, I want to make each game worth following

Around the core game, I added a main menu, an opening film, background music and a five-step offline tutorial, plus two fixed challenges: defending a capital and bringing a trailing civilization back into contention. I want a first-time player to know where to start without first learning the model configuration or a long list of rules.

My next focus is the pace of a whole game: whether the early development phase stays interesting, whether war and research both offer opportunities, and whether the later rounds still have decisions worth stepping into. Finishing a game is the baseline. I want it to let me observe AI decisions and give someone a reason to watch the next round.

For now, I am continuing it as a local single-player project; I have not opened a public online demo or released the source. Offline play has no model-call costs. Real-model play and natural-language translation require provider configuration and involve API costs.

Next project — 02Trading Simulator