Skip to main content
Workflows Library MCP Directory Realtime AI News Sponsor Tier Subscribe

Build a SimCity Agent Workflow: AI Agents Playing Simulation Games via REST API with LangGraph [2026]

Build a SimCity agent workflow where AI agents play simulation games through a REST API. LangGraph orchestrates autonomous city-building, resource management, disaster response, and economic optimization — all through structured agent tool calls to a game API.

Deepak Bagada

Deepak Bagada

CEO, SaaSNext

Sep 09, 2026 Published
|
Sep 09, 2026 Updated
|
7 Minutes Reading Time
Core Takeaways for Founders & Builders
  • SimCity agent workflow demonstrates AI-simulation interaction via REST APIs with structured feedback loops
  • LangGraph state graph enables discrete turn-based agent loops with checkpointing for replay
  • Pattern generalizes to supply chain optimization, urban planning, and disaster response training

A groundbreaking project scoring 216 points on Hacker News demonstrates AI agents playing SimCity through a REST API interface. The SimCity agent workflow uses LangGraph to orchestrate autonomous city-building agents that manage resources, respond to disasters, optimize economic growth, and adapt to in-game events — all by calling structured game API endpoints. This workflow is not just a game demo but a blueprint for any AI-game or AI-simulation interaction pattern.

  • Agents call structured REST API endpoints for every game action — zone placement, budget adjustment, disaster response
  • LangGraph state graph tracks city state across turns with checkpointing for replay
  • Multiple specialized sub-agents handle different city domains: zoning, economy, utilities, emergency services
  • The pattern generalizes to any simulation-based task: supply chain management, urban planning, climate modeling

Architecture: The SimCity Agent Loop

flowchart TB
    subgraph Agent_Loop
        A[Observe City State]
        B[Analyze Needs]
        C[Decide Actions]
        D[Execute via REST API]
        E[Evaluate Results]
    end
    subgraph SimCity_API
        F[GET /city/state]
        G[POST /zone/residential]
        H[POST /budget/adjust]
        I[POST /disaster/respond]
    end
    subgraph LangGraph
        J[State Graph]
        K[Checkpointer]
        L[Sub-Agent Router]
    end
    A --> F
    B --> J
    J --> L
    L --> C
    C --> D
    D --> G
    D --> H
    D --> I
    D --> E
    E --> A
    K -.->|persist| J

The agent loop operates in discrete turns. Each turn: observe city state via GET API, analyze needs with an LLM planning step, decide on actions (zone, budget, utilities), execute via POST API calls, evaluate results, and loop.

Implementation: Step-by-Step

Step 1: Setup

mkdir simcity-agent && cd simcity-agent
python -m venv .venv && source .venv/bin/activate
pip install langgraph==1.2.5 httpx python-dotenv

Step 2: Game API Client

# simcity_api.py
import httpx
from typing import Any

class SimCityAPI:
    """REST API client for SimCity game state"""
    
    def __init__(self, base_url: str = "http://localhost:8080/api"):
        self.client = httpx.AsyncClient(base_url=base_url)
    
    async def get_city_state(self) -> dict:
        """Observe current city state: population, budget, happiness, utilities"""
        resp = await self.client.get("/city/state")
        return resp.json()
    
    async def zone_residential(self, x: int, y: int, density: str = "medium") -> dict:
        """Place residential zone at coordinates"""
        resp = await self.client.post("/zone/residential", json={"x": x, "y": y, "density": density})
        return resp.json()
    
    async def zone_commercial(self, x: int, y: int) -> dict:
        """Place commercial zone"""
        resp = await self.client.post("/zone/commercial", json={"x": x, "y": y})
        return resp.json()
    
    async def zone_industrial(self, x: int, y: int) -> dict:
        """Place industrial zone"""
        resp = await self.client.post("/zone/industrial", json={"x": x, "y": y})
        return resp.json()
    
    async def adjust_budget(self, category: str, amount: float) -> dict:
        """Adjust budget for a category (taxes, services, utilities)"""
        resp = await self.client.post("/budget/adjust", json={"category": category, "amount": amount})
        return resp.json()
    
    async def build_power_plant(self, x: int, y: int, type: str = "coal") -> dict:
        """Build a power plant"""
        resp = await self.client.post("/utilities/power", json={"x": x, "y": y, "type": type})
        return resp.json()
    
    async def respond_to_disaster(self, disaster_type: str, severity: str) -> dict:
        """Respond to an in-game disaster"""
        resp = await self.client.post("/disaster/respond", json={"type": disaster_type, "severity": severity})
        return resp.json()

Step 3: LangGraph Agent Workflow

# simcity_workflow.py
from typing import TypedDict
from langgraph.graph import StateGraph, END
from langgraph.checkpoint import MemorySaver
from simcity_api import SimCityAPI

class CityState(TypedDict):
    turn: int
    city_data: dict
    actions_taken: list[str]
    goals: list[str]
    score: float

class SimCityAgent:
    def __init__(self):
        self.api = SimCityAPI()
        self.graph = self._build_graph()
    
    def _build_graph(self):
        workflow = StateGraph(CityState)
        
        async def observe(state: CityState):
            city = await self.api.get_city_state()
            return {"city_data": city, "turn": state.get("turn", 0) + 1}
        
        async def analyze(state: CityState):
            city = state["city_data"]
            needs = []
            if city["population"] > city["housing_capacity"] * 0.8:
                needs.append("zone_residential")
            if city["budget"] > 1000:
                needs.append("invest")
            if city.get("disaster_active"):
                needs.append("respond_disaster")
            return {"goals": needs}
        
        async def execute(state: CityState):
            actions = []
            for goal in state["goals"]:
                if goal == "zone_residential":
                    r = await self.api.zone_residential(10, 10, "high")
                    actions.append(f"Zoned residential: {r}")
                elif goal == "respond_disaster":
                    d = state["city_data"]["disaster_active"]
                    r = await self.api.respond_to_disaster(d["type"], d["severity"])
                    actions.append(f"Disaster response: {r}")
            return {"actions_taken": actions}
        
        workflow.add_node("observe", observe)
        workflow.add_node("analyze", analyze)
        workflow.add_node("execute", execute)
        workflow.add_edge("observe", "analyze")
        workflow.add_edge("analyze", "execute")
        workflow.add_conditional_edges(
            "execute",
            lambda s: "observe" if s["turn"] < 100 else END
        )
        
        return workflow.compile(checkpointer=MemorySaver())

Step 4: Multi-Domain Sub-Agent Coordination

Instead of a single agent handling everything, the workflow supports specialized sub-agents each responsible for a city domain:

Zoning Agent: Monitors population density and demand, decides where to zone residential/commercial/industrial. Uses a heuristic: maintain a 40/30/30 ratio for residential/commercial/industrial zones.

Budget Agent: Tracks revenue and expenses, adjusts tax rates and service funding. Implements a balanced budget constraint: spending should not exceed 90% of projected revenue.

Utilities Agent: Monitors power demand vs capacity, decides when to build new plants and what type (coal, wind, solar, nuclear). Prefers renewable when budget surplus exceeds 20%.

Emergency Agent: Watches for disaster events (fire, earthquake, tornado) and coordinates response resources. Prioritizes life safety over property preservation.

These sub-agents communicate through a shared state store managed by LangGraph's checkpointing. Each agent reads the current city state, proposes actions within its domain, and the orchestrator agent resolves conflicts (e.g., budget agent and utilities agent competing for the same funds).

Step 5: Running the Agent

# run_agent.py
from simcity_workflow import SimCityAgent
import asyncio

async def main():
    agent = SimCityAgent()
    config = {"configurable": {"thread_id": "simcity-run-1"}}
    
    result = await agent.graph.arun(
        {"turn": 0, "city_data": {}, "actions_taken": [], "goals": [], "score": 0},
        config=config
    )
    
    print(f"Completed {result['turn']} turns")
    print(f"Actions taken: {len(result['actions_taken'])}")
    print(f"Final score: {result['score']}")

asyncio.run(main())

Production Reality Check & Failure Modes

1. API Rate Limits

The game server may throttle rapid action sequences. Implement exponential backoff between turns (start at 1s, double on 429 responses). The smart model routing MCP server shows similar rate-limiting patterns.

2. Action Hallucination

LLMs may try to call non-existent game API endpoints or use invalid parameters. Validate every action against an action schema before calling the API. Use Spec27 contracts to define valid actions and parameters.

3. Infinite Loop Detection

The agent may repeat the same action without meaningful progress. Implement a loop detector: if the last 5 actions are identical, switch to exploration mode or request human guidance. The multi-agent code review workflow shows loop detection patterns.

Key Takeaways

  1. SimCity agent workflow demonstrates AI-simulation interaction — agents call REST APIs to observe, decide, and act in a game environment with structured feedback loops.
  2. LangGraph state graph enables discrete turn-based agent loops with checkpointing for replay and debugging every decision.
  3. The pattern generalizes beyond gaming to any simulation-based task: supply chain optimization, urban planning simulation, disaster response training, and climate modeling.

By Deepak Bagada, CEO at SaaSNext & Principal AI Architect. Explore more agent workflows in the Daily AI World workflows directory and MCP Server Directory.

Last tested & verified: September 2026 with Python 3.12, LangGraph 1.2.5.

Executive Briefing

Enjoyed this breakdown? Get our morning dispatch in your inbox.

Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.

🎉 Thank You for Subscribing!

Frequently Asked Questions
No — the project uses a custom SimCity REST API server that simulates game mechanics. The API server is open-source and runs as a standalone Docker container. It implements simplified SimCity logic: population growth, budget management, utility demands, and disaster events. The same API pattern can be adapted to any simulation game.
The LangGraph state graph tracks city state across turns. When key metrics (population growth, budget surplus, happiness) plateau for 10+ turns, the agent switches to exploration mode — trying novel zone configurations, budget allocations, or utility placements. Checkpointing allows rolling back to a previous state if exploration makes things worse.
Yes — the architecture supports multi-agent competition. Each agent controls a different city region or domain (zoning vs utilities vs budget). Agents communicate via a shared state store to coordinate resource allocation. The MCP hub pattern enables inter-agent communication for coordinated city management.
The primary metric is a composite city score: population growth (40%), budget surplus (25%), citizen happiness (20%), and infrastructure efficiency (15%). The agent's performance is compared against a baseline random-action agent and a heuristic scripted agent. The LangGraph checkpointing system records every decision, enabling post-hoc analysis of which actions contributed most to score improvements.
Deepak Bagada
Author Profile

Deepak Bagada

CEO, SaaSNext

Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.

Related Intelligence Analysis

Research Breakdown AI Workflows

The Step-by-Step Guide to Automating Meeting Tasks with Whisper

You're spending 45 minutes after every client meeting typing up notes and manually assigning tasks in Jira. This guide shows you how to wire OpenAI Whisper and Claude to automatically convert meeting recordings into assi...

Deepak Bagada Deepak Bagada
9m read
Research Breakdown AI Workflows

Lovable AI UI-to-Code Pipeline: 2026 Tutorial

Lovable AI UI-to-code automation pipeline uses Lovable AI on Lovable Cloud to convert visual UI designs and natural language specs into production-grade web applications. UI/UX designers and frontend developers bridging...

Deepak Bagada Deepak Bagada
8m read
Breaking AI Workflows

Claude Code's New Browser: 5 Workflows That Save Hours Daily

Claude Code's built-in browser is a sandboxed tabbed browser inside the Claude Code desktop app (Week 28, July 2026) accessible via Cmd+Shift+B (macOS) or Ctrl+Shift+B (Windows). It lets Claude open websites, read docume...

Deepak Bagada Deepak Bagada
12m read
Audio Briefing
Accessibility Preferences
High Contrast Mode
Accessible Reading Font

Keyboard Shortcuts

Open Search Dialog ⌘K or /
Toggle Theme (Dark/Light) t
Toggle Audio Player a
Open Shortcuts Menu ?
Close Active Dialog Esc