Sunday, August 23, 2026

Unreal Playground — Python designs a hole, Unreal and Blender build it

A golf hole should be a typed object before it is a mesh. courseforge/unreal-playground is a Python-first learning environment for AI-assisted course design. Agents write a validated CourseDesign. Unreal evaluates it in a game-engine world. Blender realizes it as a portable hole.glb. Then Python scores, critiques, and revises.

Python owns orchestration, schemas, scoring, storage, and the learning loop. Unreal and Blender are interchangeable backends — including a pure-Python fake so the whole loop runs without Docker or an editor.

The loop

prompt
  │
  ▼
DesignerAgent ──► CourseDesign JSON
  │                 (tee, green, fairway spline, hazards)
  ├──────────────► Unreal  evaluate  ──► metrics + camera PNGs
  └──────────────► Blender realize   ──► hole.glb + preview
  │
  ▼
PlayabilityAgent ──► score / critique ──► revise ──► next iteration

They are not two copies of the same renderer. Unreal answers “is this hole measurable as built?” Blender answers “what does this hole look like as geometry?” Both consume the same design object.

# Offline — no Unreal, no Docker
python python_env/orchestration/run_iteration.py \
  --prompt "short risk-reward par 4 in a Pine Barrens style" --fake

python python_env/orchestration/run_experiment.py \
  --prompt "links par 5 for a scratch golfer" --iterations 3 --fake

Each run lands under python_env/datasets/generated/<job_id>/: prompt, design JSON, job JSON, metrics, score, critique, screenshots, logs.

Where the engines actually are

Phase 1 is done. Pydantic schemas, deterministic designer/routing/critic agents, fake Unreal, run_iteration / run_experiment / run_batch, and dataset archival. The MVP hole is a short Pine Barrens par 4 you can generate on a laptop.

Phase 2 wired the CourseForge worker standard. A persistent unreal-worker runs the versioned unreal-golf-build job package. Backends swap without changing the agent graph: FakeUnrealRunner, WorkerUnrealRunner, one-shot Docker, or a host LocalUnrealRunner gated by COURSEFORGE_REAL_UNREAL=1.

Dual-engine graphics (FEAT-003) is the recent ship. Host Blender 4.x exports a real hole.glb plus Cycles preview. Host Unreal now goes past placeholder boxes:

  • fairway SplineComponent, rough and tree-line proxies
  • heightmap .r16 + real ALandscape import through a thin GolfCourse C++ bridge
  • PCG graphs + volume ISM fill when the commandlet cannot tick a StaticMeshSpawner
  • InstancedFoliage tree line from a saved foliage type

End-to-end Embabel iterations have run with both host binaries in the same job (Landscape + PCG on Unreal, glb/preview on Blender). Kind blender-worker already runs the same package over HTTP. Full in-pod Unreal golf jobs are still the open edge — the 35–49 GB worker image is a local/Kind concern, not CI.

Embabel on top of the same contracts

A Python mirror of Embabel’s nested-agent (Matryoshka) pattern lives in python_env/embabel_explore/. CourseDesignAgent nests design → evaluate → realize_blender → critique. Placement (fake / host binary / Kind worker) swaps underneath without rewriting the graph.

The gated designer studio (golf-embabel-web on port 8765) is chat + before/after, validation gates, and inline revise/redo. The same GUI can deploy into the CourseForge Kind suite. Cookbook recipes — type chaining, conditions, stuck recovery, RepeatUntil, thinking, streaming — are mapped onto this golf loop, not a second travel-agent demo.

Process sits on SDLC-SPDD canvases (FEAT-001 MVP hole, FEAT-002 worker integration, FEAT-003 dual-engine). The interesting remaining work is richer artist-authored PCG assets and a clean Kind Unreal worker run — not another schema rewrite.

Source: github.com/courseforge/unreal-playground
Related: embabel-v1-learning · SDLC-SPDD

No comments: