AI-powered geopolitical strategy simulation. Three great powers - NATO, Russia, and China - compete for territory, resources, and global dominance on an interactive map. Each faction is controlled by an AI making strategic decisions every turn.
# Install dependencies
npm install
# Configure your AI provider (see below)
cp .env.example .env
# Edit .env with your API key
# Run (starts both server + client)
npm run devOpen http://localhost:5173 and click Start Game.
War Games supports three AI providers. Pick whichever you prefer - you only need one.
- Get an API key at https://aistudio.google.com/apikey
- Set in
.env:AI_PROVIDER=gemini GEMINI_API_KEY=your-key-here
- Get an API key at https://platform.openai.com/api-keys
- Set in
.env:AI_PROVIDER=openai OPENAI_API_KEY=your-key-here
- Get an API key at https://console.anthropic.com/settings/keys
- Set in
.env:AI_PROVIDER=anthropic ANTHROPIC_API_KEY=your-key-here
Want Claude commanding NATO while Gemini runs Russia and ChatGPT leads China? Set per-faction overrides:
AI_PROVIDER=gemini # default for any faction without an override
NATO_AI_PROVIDER=anthropic # NATO uses Claude
RUSSIA_AI_PROVIDER=gemini # Russia uses Gemini
CHINA_AI_PROVIDER=openai # China uses ChatGPT
NARRATOR_AI_PROVIDER=anthropic # Narrator uses Claude
# You'll need API keys for each provider you use
GEMINI_API_KEY=...
OPENAI_API_KEY=...
ANTHROPIC_API_KEY=...| Variable | Default | Description |
|---|---|---|
AI_PROVIDER |
gemini |
Global default: gemini, openai, or anthropic |
NATO_AI_PROVIDER |
(uses global) | Override provider for NATO faction |
RUSSIA_AI_PROVIDER |
(uses global) | Override provider for Russia faction |
CHINA_AI_PROVIDER |
(uses global) | Override provider for China faction |
NARRATOR_AI_PROVIDER |
(uses global) | Override provider for the narrator |
GEMINI_MODEL |
gemini-2.5-flash |
Gemini model override |
OPENAI_MODEL |
gpt-4o-mini |
OpenAI model override |
OPENAI_BASE_URL |
https://api.openai.com/v1 |
OpenAI-compatible endpoint (for local models, Azure, etc.) |
ANTHROPIC_MODEL |
claude-sonnet-4-6 |
Anthropic model override |
The OPENAI_BASE_URL option means you can use any OpenAI-compatible API, including local models via Ollama, LM Studio, or vLLM.
Each turn (every 10-15 seconds):
- The game engine builds a briefing for each faction with visible territories, resources, units, and valid actions
- All three AI factions receive their briefing and respond with strategic orders (move, attack, fortify, recruit, trade, spy, research, diplomacy, nuke)
- Orders are resolved - combat, territory changes, resource gains
- A narrator AI writes a dramatic CNN-style news recap
- The map updates in real-time
| Faction | Leader | Strategy |
|---|---|---|
| NATO | President Trump | Tech advantage, coalition warfare, aggressive dealmaking |
| Russia | President Putin | Defensive depth, nuclear deterrence, cold calculation |
| China | President Xi Jinping | Economic leverage, patience, long-term strategy |
- Frontend: Vite + TypeScript + Leaflet map with territory overlays, combat effects, and game summary
- Backend: Express server (port 3001) with game engine and AI provider abstraction
- AI: Direct API calls to Gemini, OpenAI, or Anthropic (no agent framework - just raw API)
- State: In-memory game state, turn history stored per-game
server/
index.ts - Express server setup, dotenv loading, API routes
ai-provider.ts - Multi-provider abstraction (Gemini/OpenAI/Anthropic)
ai.ts - High-level AI functions (getFactionOrders, getNarrative)
engine.ts - Game engine, turn resolution, combat, diplomacy
types.ts - TypeScript interfaces for game state, orders, events
src/
main.ts - Frontend entry point
flatmap.ts - Leaflet map rendering, territory overlays, UI
style.css - Styles
game/
factions/ - Faction persona markdown files
initial-world.json - Starting game state and map configuration
server/engine.ts runs the turn loop:
- Build a briefing for each faction (visible territories, resources, units, valid actions)
- Call
getFactionOrders()in parallel for all 3 factions - Parse JSON responses, resolve orders (movement, combat, research, diplomacy, nukes, etc.)
- Call
getNarrative()for a dramatic news-style recap - Store turn history, broadcast state to clients via polling
- Default to cheapest models (Gemini Flash, GPT-4o-mini)
- Compact JSON format for all orders (no prose in game data)
- 15-second timeouts prevent runaway calls
npm run dev # Client + server (hot reload)
npm run server # Server only
npm run client # Vite dev server only
npm run build # Production build
npm start # Production serverMIT


