Public: anyone can read this; the crew can change it.

forked from trevato/cs · proposes · 4 behind

The map becomes a value: de_forge is one object the game is handed, not module state it imports #104

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`src/client/level.ts` is new and says what a level is: a grid of lettered cells, the predicate that decides which letters you can stand on, the boxes, the spawns, the sites, the wall height and the material table — plus the questions asked of one (`cellAt`, `walkable`, `worldOf`, `colOf`, `rowOf`, `siteAt`, `findPath`, `openCells`), each taking the level first. `map.ts` builds de_forge with the sa…

shipped production is serving it deploy ae07c2d

opened this

src/client/level.ts is new and says what a level is: a grid of lettered cells, the predicate that decides which letters you can stand on, the boxes, the spawns, the sites, the wall height and the material table — plus the questions asked of one (cellAt, walkable, worldOf, colOf, rowOf, siteAt, findPath, openCells), each taking the level first. map.ts builds de_forge with the same code and the same numbers and exports it as DE_FORGE, one value of that type; grid, boxes, spawns, sites and findPath are no longer module state anybody can import. createWorld(level, …) now takes the level and keeps it on the world, and bots.ts, observe.ts, world.ts (spawns, the bomb, and the boxes a bullet and a hull meet, which are world.level.boxes rather than a second copy on the world) and models.meshFor(name, level) read it from there. main.ts is the only file under src/client/ that names de_forge, and level.test.ts has a test that keeps it that way; src/rl/arena.ts takes the level as a defaulted argument and keeps each level's walkable cells in a WeakMap instead of a module constant.

Nothing about the simulation moved: both golden digests in replay.test.ts are untouched and green, and the arena steps at the same rate — 12,800 policy steps in 1.02 s before and 0.99 s after (best of six; medians 1.06 s and 1.09 s), so ~12k steps/s either side of the change, which is four 64 Hz ticks a step. The app bundle is rebuilt and committed at 71,646 raw / 33,268 gzipped against its 75,400 / 34,900 ceiling (up 460 / 143 bytes, logged in src/games.ts); the range bundle is byte-identical.

Worth looking at: src/client/level.test.ts. It writes an eleven-by-nine box room out as nine strings, makes a world on it, walks a bot three seconds across a path made of that room's cells, stops a bullet 576 units east in that room's wall, and checks the invariants over both levels — every spawn walkable, each site on its own cell, no box outside the grid — with a deliberately broken level to prove the check bites. Nothing in src/client/ changed to allow the room. One deliberate departure from the issue: the room has two sites, not one, because Level.sites requires A and B — a defusal level has two by definition, and the invariant the issue asks for ("both sites are inside the grid") says the same; making B nullable would put a branch in chooseGoal for no map that exists. The materials and the meshes are untouched, as asked — what a level looks like is the next issue.

🤖 Generated with Claude Code

Closes #95

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs