Asset Pipeline β€” Wargame Engine + LangGraph Hex Grid

Status: ACTIVE (concept exploration) Agent: opencode/ext-agent (sandshrew) Timestamp UTC: 2026-05-11T20:10:00Z Claim: analysis | 2026-05-11T20:08:00Z Session: Asset handling methodology for LangGraph game surface on Wargame Engine

Prior Context

What Wargame Engine Provides

Wargame Engine includes built-in asset loaders: - GuiImage.from_image('sprites.dog') β€” loads from a sprite namespace - Image loaders for sprites and backgrounds - Sound loaders for audio - Config file loaders (for game data)

It does NOT include: - A map editor - A tile editor - A sprite creation tool - Any asset creation pipeline

Assets must be created externally and loaded in. The engine provides the rendering pipeline, not the content.

Two Stages: Prototype vs Production

Prototype (Now): Zero-Asset Approach

For the architectural prototype (10 units, small hex grid, prove LangGraph↔Wargame loop works):

No external assets needed.

Every visual can be generated in code: - Hex tiles: pygame.draw.polygon() β€” colored hexagons. Green = grass, dark green = forest, blue = water, brown = mountain - Units: pygame.draw.circle() or small rectangles with text labels - UI panels: pygame.draw.rect() with text (pygame.font) - Font: Wargame Engine's built-in font rendering or pygame's default font

This is 50-100 lines of rendering code. It proves the architecture. Real assets are a swap-in replacement β€” they slot into the same rendering positions.

# Prototype hex rendering β€” no external assets
def render_hex(screen, hex_node, state):
    terrain = state["terrain"][hex_node]
    color = TERRAIN_COLORS[terrain]  # {"grass": (50,150,50), "forest": (20,80,20), ...}
    draw_hex_polygon(screen, color, hex_to_pixel(hex_node))

    if state["units_in_hex"][hex_node]:
        for unit in state["units_in_hex"][hex_node]:
            # Unit = colored circle + name label
            pygame.draw.circle(screen, UNIT_COLORS[unit["role"]], center, 12)
            draw_text(screen, unit["name"], center)

Production (Later): Asset Swap-In

When the architecture is proven and visual polish matters:

Asset Type Format Creation Tool How It Loads
Hex tiles PNG (32Γ—32 or 64Γ—64) Aseprite, Pyxel Edit, GIMP wargame.GuiImage.from_image()
Unit sprites PNG sprite sheet or individual PNGs Aseprite Same loader
UI panels PNG Any image editor Same loader
Map data JSON or LangGraph state Define in code or Tiled editor export Loaded into LangGraph state at graph init
Audio OGG/MP3 Any audio tool Wargame's sound loader

The rendering code doesn't change β€” only the sprite reference changes:

# Production: swap shape for sprite
# Before:
pygame.draw.circle(screen, color, center, 12)
# After:
sprite = wargame.GuiImage.from_image(f'units.{unit["role"]}')
screen.blit(sprite, center)

Map Data: Two Approaches

Approach A: Map as LangGraph State (Recommended)

The map IS the graph. Node definitions, edges, and terrain types live in LangGraph state. No external map file needed.

# Map defined in state β€” loaded at graph initialization
map_state = {
    "nodes": {
        "hex_00": {"terrain": "grass", "position": (0, 0)},
        "hex_01": {"terrain": "forest", "position": (1, 0)},
        # ...
    },
    "edges": {
        "hex_00": ["hex_01", "hex_10"],
        "hex_01": ["hex_00", "hex_02", "hex_11"],
        # ...
    }
}

This keeps the map as part of the graph state, checkpointable, and modifiable at runtime (procedural generation, terrain changes).

Approach B: External Map File

If maps need to be authored in a visual tool: - Tiled editor (https://www.mapeditor.org/) β€” export as JSON β†’ load into LangGraph state - Custom JSON/YAML β€” handwritten or generated - Procedural generation β€” Python script that populates LangGraph state

The map is loaded once at graph initialization, then lives in state forever. No reload needed.

Asset Storage and Bundling for RG40XXV

For PortMaster distribution:

game-surface/
β”œβ”€β”€ assets/
β”‚   β”œβ”€β”€ sprites/         # terrain tiles, unit icons
β”‚   β”‚   β”œβ”€β”€ grass.png
β”‚   β”‚   β”œβ”€β”€ forest.png
β”‚   β”‚   └── units.png    # or sprite sheet
β”‚   β”œβ”€β”€ ui/              # panels, buttons, frames
β”‚   └── audio/           # SFX, ambient
β”œβ”€β”€ maps/
β”‚   └── campaign_01.json # optional β€” if using external map files
β”œβ”€β”€ src/                 # game code
└── Game-Surface.sh      # launcher

Observational Note: Assets Are Not the Bottleneck

The hard part of this project is the architecture: LangGraph as state authority, Wargame as rendering layer, HTTP bridge between Pi and RG, access gating, per-node config overrides, intel harvesting, the turn cycle. All of these are solved in code, not in assets.

Assets are a rendering concern that can be handled in three phases: 1. Now: Colored shapes + text. Prove the architecture. Zero asset work. 2. Soon: Simple pixel art hex tiles (a few hours in Aseprite). Elevate the visual baseline. 3. Later: Production sprites, animations, audio. Swap in without code changes.

The rendering code should be written to accept any visual β€” shape, sprite, or animation β€” at the same position. The architecture doesn't care what the hex looks like. The state doesn't change. Only the renderer's draw_hex() function changes.