- TypeScript 100%
| src | ||
| .gitignore | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
AI Game Engine
A universal text-based game engine where an LLM acts as the game master — narrating events, simulating world mechanics, generating emergent crises, and persistently tracking all game state through tool calls. The engine is genre-agnostic: it provides the game loop, AI integration, state management, and persistence. The actual setting, rules, and tone are defined entirely by a system prompt and an initial state file, so you can run anything from a space 4X strategy to a medieval fantasy RPG to a cyberpunk noir mystery.
It ships with a bundled example: a hard sci-fi 4X space strategy scenario where the player commands "United Earth" — a newly unified human government on the cusp of interstellar expansion.
Built with TypeScript and the OpenRouter SDK. The AI receives the full game state plus recent turn history as context, then uses function-calling tools to surgically update the world — modifying stats, spawning new entities, deleting destroyed ones, and advancing the clock — while rendering narrative output in Markdown directly in the terminal.
Features
- AI-Driven Game Master — An LLM acts as narrator, referee, and world simulator. It generates the situation report, presents decision options, and executes all state mutations via structured tool calls.
- Genre-Agnostic — The engine has no built-in genre. The system prompt (
src/prompt.txt) and initial state (src/state/state.ts) define the setting, rules, and tone. Swap them out to run an entirely different game — no engine code changes required. - Persistent Game State — All entities (empires, planets, characters, items, quests — whatever the scenario defines) are tracked in a dynamic JSON state object. State survives across sessions via automatic save/load to
save.json. - Function-Calling Tools — The AI uses three tools to maintain the world:
update_game_state— Set fields, add/remove array entries, advance the in-game clock, log notable events.spawn_object— Create new entities (newly built ships, discovered locations, emerging characters, researched tech).delete_object— Permanently remove destroyed or obsolete entities.
- Short-Term Memory — The last N turns (configurable) of player commands and GM narratives are injected into the system prompt as context, giving the AI continuity without re-sending the full transcript.
- Terminal Markdown Rendering — Narrative output is parsed with
markedand rendered withmarked-terminalfor a clean in-terminal reading experience.
Tech Stack
| Layer | Technology |
|---|---|
| Runtime | Node.js (ESM) |
| Language | TypeScript (strict mode) |
| AI Provider | OpenRouter SDK (@openrouter/sdk) |
| Terminal I/O | prompt-sync |
| Markdown | marked + marked-terminal |
Getting Started
Prerequisites
- Node.js
- An OpenRouter API key
Installation
git clone <repo-url>
cd ai-game-engine
npm install
Configuration
Edit src/config.json:
{
"apiKey": "YOUR_OPENROUTER_API_KEY_HERE",
"model": "z-ai/glm-4.5",
"provider": "decart/fp8",
"temperature": 1.0,
"shortTermMemorySize": 5,
"savepath": "save.json",
"startDate": "2025-01-20T20:00:00Z",
"startEvent": "2099-05-04: The first earth orbital spaceport with ship build capabilities has been finished."
}
| Field | Description |
|---|---|
apiKey |
Your OpenRouter API key |
model |
The LLM model identifier to use as the game master |
provider |
OpenRouter provider routing preference |
temperature |
Sampling temperature for the AI |
shortTermMemorySize |
Number of recent turns to inject into the system prompt |
savepath |
Path to the save file (created/loaded automatically) |
startDate |
Initial in-game datetime |
startEvent |
First entry in the notable events log |
Creating Your Own Scenario
The engine is driven by two files:
src/prompt.txt— The system prompt sent to the LLM. This defines the game's genre, setting, lore, rules, tone, and turn structure. Rewrite it to change the entire game.src/state/state.ts— The initial game state object. This is the starting world the AI will mutate. Define whatever entities make sense for your scenario — empires and planets for a 4X game, NPCs and locations for an RPG, suspects and clues for a mystery, etc.
The IState interface (src/state/types.ts) is intentionally minimal (datetime, notableEvents, items) — the items dictionary is untyped and accepts any shape, so your scenario's entities can have any fields.
Running
npm start
This compiles the TypeScript and launches the game. If no save file is found, the game initializes a fresh campaign using the initial state. Type quit at any prompt to exit.
Project Structure
ai-game-engine/
├── src/
│ ├── index.ts # Entry point — game loop, player input, save/load cycle
│ ├── game.ts # Turn processing — AI call, narrative display, tool-call execution
│ ├── ai.ts # OpenRouter client, system prompt assembly, AI generation
│ ├── tools.ts # Function-calling tool definitions (update/spawn/delete)
│ ├── storage.ts # Save/load to JSON, short-term memory buffer
│ ├── config.ts # Config loader (imports config.json)
│ ├── config.json # Runtime configuration (API key, model, save path, etc.)
│ ├── prompt.txt # The game master system prompt (lore, rules, turn format)
│ ├── types.ts # Shared type definitions (IShortTermMemory)
│ └── state/
│ ├── state.ts # Initial game state (scenario-specific entities)
│ ├── types.ts # IState interface (datetime, notableEvents, items)
│ └── index.ts # Barrel export
├── package.json
├── tsconfig.json
├── LICENSE # GPL-3.0-only
└── README.md
How It Works
- Game Loop (
index.ts) — Prompts the player for input, passes it toprocessPlayerTurn, then saves after every turn. - Turn Processing (
game.ts) — Assembles the system prompt (game rules + recent memory + current state), calls the AI, renders the narrative as Markdown, then iterates over any tool calls the AI returned and applies each mutation to the live state object. - AI Layer (
ai.ts) — Uses the OpenRouter SDK to send the system prompt and player input as a two-message chat, with the three game tools attached. - State Management (
state/) — A single mutablestateobject holdsdatetime,notableEvents, anditems(a dictionary of all game entities keyed by ID). The AI modifies this object exclusively through tool calls. The shape ofitemsis defined by the scenario, not the engine. - Persistence (
storage.ts) — After every turn, the full state plus short-term memory is serialized tosave.json. On startup, if the save exists, it's loaded back into the state object.
License
GPL-3.0-only