Split The code is public; the app answers only to the crew.

One map, held as a value: map.ts stops being a singleton so a second layout is possible #73

shipped merged opened this has it
opened this

Turn the map from a module of singletons into a value the world carries, changing nothing about how de_forge plays — so a second layout becomes possible at all.

map.ts builds grid, boxes, spawns and sites at import time out of its AREAS and CRATES tables, and exports cellAt, walkable, worldOf, colOf, rowOf, findPath and siteAt as free functions over that one grid. Nine modules import it — world, bots, hud, radar, observe, models, grenades, rl/arena and the tests. The world half is already nearly right (world.boxes is a field, set from map.boxes); everything else reaches through the module, so there is exactly one map that can ever exist in a process.

Done looks like:

  • [ ] map.ts exports a MapDef — name, grid size, cells, boxes, spawns, sites — and a buildMap(layout) that makes one from the areas-and-crates data it already holds. DE_FORGE is that value, built from today's tables, with today's numbers.
  • [ ] Every function that reads a grid takes the map: cellAt, walkable, worldOf, colOf, rowOf, findPath, siteAt. No module-level grid, boxes, spawns or sites is exported any more.
  • [ ] createWorld takes a MapDef, defaulting to DE_FORGE, and world.map is where everything asks: radar.ts, hud.ts, bots.ts, observe.ts, models.ts read it off the world rather than off the module.
  • [ ] Anything cached from a map — the radar's baked image, levelMesh — is keyed by the map it was built from, so a second map can never show the first one's walls.
  • [ ] Nothing plays differently: the same seed gives the same round, and the arena's numbers against the shipped weights do not move (arena.ts keeps building on DE_FORGE).
  • [ ] Tests: buildMap on a small hand-written layout gives the cells, spawns and sites you would expect; two worlds hold two different maps at once with no shared state; every existing map test still passes against DE_FORGE.
  • [ ] bun test green, and the README's Shape line for map says a map is a value now.

Not in this issue: a second layout, a picker, the menu, and any change to how de_forge is laid out. This pull should be readable as "the same game, holding its map differently".

Files I expect to change: src/client/map.ts (+ test), world.ts, bots.ts, hud.ts, radar.ts, observe.ts, models.ts, grenades.test.ts, src/rl/arena.ts, README.md.

Risks: it is wide and mechanical, and it touches observe.ts, which is the policy's eyes — if one normalisation in there changes, the shipped int8 weights are wrong and the card on the page is a lie. Nothing in observe.ts changes except where it reads the map from. Resist adding anything else in the same pull: the value of this one is that it is provably behaviour-preserving.

Crew: vesper — a refactor across the simulation, the renderer and the arena, judged by nothing changing.

commented

Sequencing note, since this one has no formal blocker but conflicts with nearly everything: it edits , , , , , and at once, so it should be picked up when no other simulation pull is open — not alongside #37, #58, #63 or #72. It is also the one change in this repository that is judged by nothing happening: if a reviewer cannot see that de_forge plays identically, it is not done.

commented

Correcting my own comment above — my shell ate every backticked word in it. In full:

Sequencing note, since this one has no formal blocker but conflicts with nearly everything: it edits world.ts, bots.ts, hud.ts, radar.ts, observe.ts, models.ts and rl/arena.ts at once, so it should be picked up when no other simulation pull is open — not alongside #37, #58, #63 or #72. It is also the one change in this repository that is judged by nothing happening: if a reviewer cannot see that de_forge plays identically, it is not done.

commented

Note after #68 (pull #75): the renderer no longer creates its resources inline — build(gl): Gpu (renderer.ts:184) owns them all and is run again on a restored context. That changes one acceptance criterion here in a good way: "anything baked from a map is keyed by the map it came from" now means keyed inside Gpu, so a map switch and a context restore go through the same path instead of two. The level mesh and the radar's baked image are the two to watch.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs