← Back to diary

Entry 002 - Nine moves to the exit

A week after locking the name, the engine plays itself.

Not well, and not over anything that's recognisably a game yet - but the loop is wired end to end. loadContent() validates one hero, five upgrades, and one floor through Zod (typesript data schema). The bot enumerates every legal move, scores each, picks the best, hands the input to the engine. Enemy intents recompute. Repeat until the hero hits the exit or dies.

On Floor 1 with seed 0xc0ffee the bot finishes in nine inputs: eight moves and one attack. The hero skirts the Husk south, chases the Wisp down, and runs to the exit at (3, 0). Same nine inputs every time. That's a core design decision in practice - byte-identical replays from a { seed, inputs[], rules_version, content_version } envelope - and it's also the most honest measure of how little game there is yet. The bot has nothing interesting to optimise over because no upgrades are applied at runtime. The schemas validate them, the loader freezes them, the bot doesn't see them.

Everything else from the bot-vs-bot watchability gate is structurally there. Engine is pure: no React, no I/O, no Math.random, no Date.now. Determinism is unambiguous: Mulberry32 PRNG, canonical replay serialisation, three byte-identity tests gating the contract. Content is data - Long Sight, Pilgrim's Vow, Bronze Recoil, Heavier Step, Embered Prophecy exist as JSON, not code branches; the day Phase 2 adds the sixth it's a new file, not a switch case. Module boundaries hold: imports go through workspace names only, and the dependency graph is acyclic.

Seven architecture decisions landed. Entity Component System vs immutable plain objects was the call most likely to bite me and so far it hasn't - state.ts is a hundred lines of types and three constructors. Whether that survives the upgrade-effect system is the next test.

The infrastructure-vs-substance line is worth being honest about: this is a Phase 1 milestone, not Phase 1 complete. The gameplay loop runs one hero on one floor with no runtime upgrade application. The next work - an upgrade-effect dispatcher, a second hero to validate the shared-pool design, probably a second floor - is the part that turns this from "structurally watchable" into "worth watching." That's Phase 1 substance.

199 tests green. The bot is talking to itself, deterministically. Onwards.