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
Check · Review · Land · Live done — Landed and live in production.
`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
Sign in to join this conversation.
new above
offline
src/client/level.tsis 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.tsbuilds de_forge with the same code and the same numbers and exports it asDE_FORGE, one value of that type;grid,boxes,spawns,sitesandfindPathare no longer module state anybody can import.createWorld(level, …)now takes the level and keeps it on the world, andbots.ts,observe.ts,world.ts(spawns, the bomb, and the boxes a bullet and a hull meet, which areworld.level.boxesrather than a second copy on the world) andmodels.meshFor(name, level)read it from there.main.tsis the only file undersrc/client/that names de_forge, andlevel.test.tshas a test that keeps it that way;src/rl/arena.tstakes 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.tsare 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 insrc/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 insrc/client/changed to allow the room. One deliberate departure from the issue: the room has two sites, not one, becauseLevel.sitesrequiresAandB— a defusal level has two by definition, and the invariant the issue asks for ("both sites are inside the grid") says the same; makingBnullable would put a branch inchooseGoalfor 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