The setup
A personal Godot 4.6 + C# (.NET 8) project — narrative sandbox, single player, side-on, procedurally generated world. The gameplay loop is deliberately small. What it is really an excuse for is figuring out how modern engine-driven game development should change when the developer is working alongside an AI assistant that cannot see the game window.
Two design choices fall out of that, and both ended up being independently worth doing.
Two parallel deterministic grids as the source of truth
The world is two 2D arrays:
- A
TileType[,]terrain grid (stone, soil, rope, etc.) - A
MoleculeType[,]atmospheric grid (O₂, CO₂, water vapor, etc.)
Both are seeded from a single world seed and reproduced bit-for-bit on re-generation. The visual scene is a projection of those grids, not their authority; if you regenerate the grids you regenerate the world.
The biome-aware procedural generation that fills the grids has one
non-negotiable property: per-column generation depends only on
(seed, x, layout). Column at x = 4096 is generated identically
whether or not column at x = 4095 has been generated, whether or not
the player has visited any other column, whether or not other layers have
been generated yet. Disjoint seed offsets across layers (terrain, surface
features, sky, atmosphere) keep the layers independent.
The reason matters: with this property, the world is ready to be streamed — generated lazily as the player moves — without that change being a refactor. The mathematical guarantee is already in the generator; the only thing missing is the cache layer that hands columns out on demand.
It is also the property that makes the determinism testable.
A headless determinism test wired into CI
Godot can run with --headless and a target scene. I have a scene whose
entire job is:
- Generate the world from a fixed seed.
- Generate it again with the same seed.
- Hash both grids and compare.
exit 0if they match,exit 1if they don’t.
That’s it. The whole thing runs in a few seconds and is exactly the kind
of test CI is good at. The first time a refactor accidentally introduces
non-determinism — a DateTime.Now call, a HashSet iteration order, a
Parallel.For over uncommutative side effects — the CI step turns red.
Determinism stops being an aspiration and starts being a property of the
codebase.
The HTTP introspection server
This is the piece I think is most likely to spread.
Modern AI-assisted programming has a blind spot: the assistant cannot see the game running. It can read source, it can read logs, it can read a crash dump. It cannot look at the screen and tell you that the player character is one tile inside a wall. For most software this doesn’t matter; for game development it matters constantly.
So in debug builds — and only debug builds, the export filter strips it
out of releases entirely — I run a tiny HTTP server on
127.0.0.1:9080. It is read-only and binds only to loopback. It exposes:
GET /— an index of available endpoints with descriptions.GET /world— world parameters and current seed.GET /player— player position, velocity, current state-machine state.GET /tile?x=&y=— what tile is at this cell, including its derivation chain.GET /tree— the live scene tree, including each node’s C# type and the script path that defines it.GET /screenshot— a PNG render of the current viewport.
When something goes wrong, an AI assistant can — without leaving its read-only tool surface — query the world state, look at the screen, and form hypotheses with the same information I have when I am sitting at the machine. The debug session shifts from “describe to the assistant what you are seeing” to “ask the assistant to look at what you are both seeing.” Even when I am the only one in the loop, having a machine-readable view into the running game has paid for itself repeatedly: trivial scripting to compare states across builds, automated screenshot regression for UI changes, scriptable repro of difficult bugs.
The contract drift problem is the obvious risk — endpoints rotting as the gameplay code evolves underneath them. The discipline is that the introspection server lives next to the gameplay code, not in a separate “debug” project, and changing the contract is part of the same patch that changes the underlying state. The same way a public API would be treated.
Code hygiene that ages well
A few details that aren’t load-bearing for the post but are worth stealing:
Godot.NET.Sdk/4.6.3, Roslyn analyzers raised toRecommendedwithEnforceCodeStyleInBuild=true. Warnings are not treated as errors — prototype gameplay code shouldn’t fail to build on a missing XML comment — but the analyzer voice is loud enough that bad patterns get caught early.- Statically-typed gameplay layers (alignment-meter narrative system, dialogue tree, inventory, hotbar, choice dialog) built on Godot UI controls, with C# enums and DTOs everywhere a string could plausibly have lived. The C# / engine boundary is the place this matters most: GodotObject-vs-Node-vs-Resource is a rich enough taxonomy to confuse yourself with.
- Documentation tiered three ways:
README.mdfor what is built and how to run it;docs/design-vision.mdfor what the project is for, with explicit “Decided” vs “Open design questions” markers;CLAUDE.mdfor the operational / agent guidance that has no other home. Three artifacts, three audiences.
Closer
The HTTP introspection server is a small piece of code that punches above its weight because it bridges the gap between an AI assistant’s read-only world view and the fundamentally visual nature of game development. Determinism makes it possible to reason about that world view at all. The two together turn “AI-assisted game development” from an unsatisfying mismatch into a genuinely productive loop. If you are building anything graphical and working with AI assistants, this is the pattern I would steal first.