Engine Evaluation — LangGraph Game Surface Rendering Layer
Status: ACTIVE (research synthesis) Agent: opencode/ext-agent (sandshrew) Timestamp UTC: 2026-05-11T19:20:00Z Claim: synthesis | 2026-05-11T19:15:00Z Session: MjF engine evaluation request — pygame-based RTS/strategy engines for RG40XXV rendering layer
Prior Context
- [[langgraph-game-surface]] — Game backend architecture
- [[langgraph-hex-node-mapping]] — Hex-to-node mapping concept
- Constraint: ~10 units max at outset, so performance is not the bottleneck
- Primary target: RG40XXV (ARM Linux, muOS/Knulli), pygame-based, 2D
- LangGraph owns state — engine is pure rendering/input layer
Engine Survey
Wargame Engine — Best Fit
Repo: maximinus/wargame Stack: Python 100%, Pygame Status: Active (52 commits, last updated Jul 2024, 27 stars) License: GPL-3.0 Pip installable: Yes
Architectural alignment with LangGraph:
| Wargame Feature | LangGraph Mapping |
|---|---|
| Node-tree viewport | LangGraph nodes = viewport nodes. Each graph node can render as a viewport node |
| Scene system | Discrete game phases (planning, execution, resolution). Switch scenes per phase |
| Messaging system | Decoupled state update propagation. Engine sends "clicked hex_17" message, LangGraph processes, state updates push back |
| Save/load/replay | Complements LangGraph checkpointer. Engine handles file serialization, LangGraph handles state persistence |
| GUI system (built-in) | interrupt() prompts rendered as GUI dialogs. Intel suggestions as sidebar/overlay |
| Tween animations | Unit movement animations between nodes, state transition visual feedback |
| Terminal (runtime REPL) | Debug interface for inspecting LangGraph state during development |
| Automatic config loading | Unit configs, node access lists loaded from config files |
| Logging system | Trace LangGraph node execution alongside engine events |
| Board wargame domain | Turn-based, tile/hex-based, tactical — exactly the game paradigm |
Key quote from README:
"A tree of nodes is used as a viewport, with each node usually representing either a view of sprites, or the sprites themselves."
This is the engine explicitly designed around a node-tree viewport. The LangGraph graph IS a node tree. The mapping is architectural, not metaphorical.
Example integration sketch:
# LangGraph owns state, Wargame Engine owns the view
def game_loop():
controller = wargame.engine.init(os.cwd())
# Build scene from LangGraph state
scene = build_scene_from_langgraph_state(graph.get_state(config))
controller.add_scene('game', scene)
# Override input handler to fire LangGraph invocations
controller.on_click = lambda pos: graph.invoke({
"action": "move",
"target": screen_to_hex(pos)
}, config)
# Post-invoke: rebuild scene from updated state
controller.on_post_update = lambda: rebuild_scene_from_state(...)
controller.run('game')
Risk: Opinionated engine — owns the main loop. If LangGraph's invoke/stream pattern conflicts with the engine's internal loop, integration may require fighting the engine's assumptions. The "not a library" design means less flexibility. Mitigation: the messaging system is designed for decoupling — the engine may be more hackable than it appears.
Sequtus — RTS Pattern Reference
Repo: Teifion/sequtus Stack: Python 100%, Pygame Status: Dead (391 commits, last updated May 2012, 24 stars) Note: Python 2.6 — would need porting to Python 3
Value: Reference implementation, not an engine to build on. Has well-structured separation between engine/ and game/ directories. Demonstrates:
- Unit selection (single, shift, drag, double-click, control groups)
- Order queues and build queues
- Minimap with click-to-scroll
- Collision detection
- Teams, unit life/death, tech trees
- HP bars and animated units
Not suitable as foundation due to Python 2 and 14-year staleness. But the engine/ directory (~15 files) is worth studying for RTS patterns in Pygame: selection logic, minimap rendering, scrolling, order queuing.
OpenRTS — Historical Reference Only
Location: pygame.org project #189, SourceForge (arrakis.sf.net) Status: Abandoned since 2006 Stack: Python + Pygame Features: Isometric graphics, networked multiplayer, customizable rulesets and tilesets
Value: The tile/ruleset customization concept is conceptually relevant — a ruleset is essentially a LangGraph node config. But the code is 20 years old, Python 2-era, and dead. Reference only for isometric tile rendering patterns.
Fabula — Not Found
Search across GitHub and PyPI returned no results. The user's description (client-server architecture, event-based protocols, pygame graphical editor) sounds promising but the project could not be located. Likely a niche/private project, renamed, or described from memory with an incorrect name. Skip unless a specific URL surfaces.
Godot — Overkill, Wrong Platform
GDScript (Python-like) with dedicated IDE. Extremely capable but: - Not Python — would need to bridge GDScript ↔ LangGraph's Python runtime - Runs its own editor, its own scene tree, its own everything — would fight LangGraph, not complement it - Heavy for RG40XXV - The effort-to-fit ratio is poor for a 10-unit prototype
Panda3D — Wrong Domain
3D engine with Python scripting. Overkill for a 2D hex/tile strategy game. Wrong fit.
OpenRA — Wrong Runtime
C# (79%), Lua (16%). Cannot integrate with Python LangGraph. Map format and trait system are conceptually borrowable but the engine itself is unusable.
Recommendation
Wargame Engine is the best-fit rendering layer. Its node-tree viewport, scene system, messaging architecture, and board wargame domain align with the LangGraph graph model at an architectural level, not just a metaphorical one. The engine is small (52 commits), Python/pygame, pip-installable, and maintained as of 2024. It runs on any platform pygame supports (including ARM Linux on RG40XXV).
Fallback: If Wargame Engine proves too opinionated (owns the game loop, resists LangGraph's invoke-driven state model), fall back to raw pygame or Python Arcade. The cost is having to rebuild the node-tree viewport, scene management, and GUI system from scratch — but the prototype scope (10 units, turn-based) makes this tractable.
Sequtus is worth a skim for its Pygame RTS patterns (selection, orders, minimap) but not as a foundation.
Integration Risk: The Main Loop Collision
The critical integration question is whether Wargame Engine's main loop can accommodate LangGraph's invoke-driven state model:
- Wargame's loop:
controller.run('scene')— continuous frame loop, reads input, renders - LangGraph's loop:
graph.invoke()/graph.stream()— discrete super-steps, returns when graph halts or interrupts
Observed resolution paths:
1. Interrupt-driven: LangGraph calls interrupt() → engine shows prompt → human clicks → Command(resume=...) → LangGraph continues → engine re-renders. This maps naturally — the engine's event loop IS the interrupt handler.
2. Polling: LangGraph state is queried each frame. Engine renders whatever state exists. On click, invoke() runs synchronously (blocking). For turn-based with 10 units, the blocking time is negligible.
3. Frame-skip: If invoke() is slow (LLM call inside node), engine shows a "processing..." overlay or loading screen via Wargame's GUI system.
The messaging system is the bridge: engine sends "input event" messages → LangGraph node receives → LangGraph returns state update → engine rebuilds view from new state. This is the cleanest decoupling pattern and Wargame Engine was designed for it.
Open Questions
- Does Wargame Engine's
controller.run()loop block in a way that prevents LangGraph from running its super-steps? Or can the loop be hijacked? - Can Wargame Engine's node-tree viewport map hexes dynamically (spawning viewport nodes at runtime) or must viewport nodes be pre-defined?
- Does Wargame Engine's save/load system conflict with LangGraph's checkpointer, or can they coexist (engine saves visual state, LangGraph saves game state)?
Addendum: Fabula — Found and Evaluated
Source: pygame.org/project-fabula-1746-3160.html Author: Florian Berger (fberger) Last release: 0.8.3, June 2012 (14 years dead) Stack: Python 3, Pygame, clickndrag library Distribution: Tarballs from personal static host (no GitHub, no PyPI)
Architectural Fit
Fabula's architecture is the closest conceptual match to the LangGraph game surface model of any engine surveyed:
| Fabula Feature | LangGraph Mapping |
|---|---|
| Client-server architecture | LangGraph = server (state/rules), pygame on RG40XXV = client (rendering) |
| Event-based protocol | Client emits {"action": "move", "target": "hex_17"} → LangGraph node receives → state update returns → client re-renders |
| Visualization-agnostic world model | LangGraph owns world state independent of renderer. Same state powers any client (RG40XXV, Wii canvas, PS2 homebrew) |
| Plugin system for game rules/entities | New LangGraph nodes as plugins that extend graph topology |
| Recording and playback | Complements LangGraph checkpointer for time-travel and review |
| Multi-threaded server | LangGraph's concurrent Send fan-out for parallel unit execution |
| Pygame-based graphical editor | Visual graph editor for placing/configuring nodes |
Relevance (Despite Dead Code)
Fabula's code is not usable — 14 years of Python 3 drift, tied to a dead clickndrag library, distributed as personal tarballs. But its architecture documentation and design intent serve as a blueprint for how to separate LangGraph (server/state/rules) from the rendering layer (client/input/display).
The central insight: treat LangGraph nodes as event handlers in a client-server protocol. The renderer knows nothing about game rules — it only knows how to draw state and emit events. LangGraph knows nothing about rendering — it only knows how to process events and return state updates.
Synthesis: Wargame Engine + Fabula Pattern
| Layer | What to Use | Source |
|---|---|---|
| Rendering | Wargame Engine's node-tree viewport, scene system, GUI | Wargame Engine (alive, 2024) |
| Integration pattern | Client-server event protocol, visualization-agnostic world model | Fabula (dead, 2012, conceptual only) |
| Save/load | Both — Wargame handles visual state serialization, LangGraph checkpointer handles game state | Combined |
| Input handling | Wargame's input system → serialized as events → dispatched to LangGraph | Wargame for capture, Fabula pattern for dispatch |
Updated Ranking
| Engine | Code Viability | Conceptual Fit | What to Steal |
|---|---|---|---|
| Wargame Engine | Alive (2024) | Node-tree matches graph | Rendering code, GUI, scene system |
| Fabula | Dead (2012) | Client-server matches exactly | Architecture pattern, event protocol design |
| Sequtus | Dead (2012) | Low | RTS patterns (selection, orders) |
| OpenRTS | Dead (2006) | Low | Isometric tile concepts |
| Godot | Alive | Wrong runtime (GDScript, not Python) | — |
| Panda3D | Alive | Wrong domain (3D) | — |
| OpenRA | Alive (C#) | Wrong runtime, design patterns borrowable | Trait system mental model, map format concepts |