glytch/fktry/src/sim/testkit.ts
LANE-SIM 039eff15fa [sim] research + gating, spatial cooling, exported constants, reference v4
Round 4 per lanes/LANE-SIM.md.

- research v1: kind:'lab', setResearch, snapshot.research, 'researched'.
  Gating: any id a tech unlocks is refused until that tech lands; ids no
  tech names are free forever. Labs take only what the active tech still
  needs; several labs feed one total; research survives save/load as a
  RULE, so a restored save can still build what it unlocked
- realtech.test.ts: a cold-start research run on DATA's own tree -- ore ->
  packs -> archaeology lab -> tech -> a refused recipe now accepted. Also
  the tripwire if the tech bootstrap ever closes over again; it was
  circular and unenterable at the start of this round
- spatial cooling: coolRadius, chebyshev from the footprint, auras sum,
  total capped at HEAT_COOL_MAX (0.05 -- past that a stack buys nothing
  but still costs bandwidth and floor). Proven by measuring shed rate
- src/sim/constants.ts: 12 constants so DATA imports truth instead of
  hand-copying numbers that go stale in silence
- reference v4: anchor-slab overflow tap, so the demo ships slabs (18 per
  20k). Needed TWO melt lanes: with one, melt spills onto the slab lane
  and the splitter phase-locks with the reactor's push order -- slab to
  the assembler, melt to the uplink, forever, and no slab ever sold. It
  passed 20k ticks looking perfect. referenceFactoryTech() derives the
  techs the layout needs, from the commands
- also wired: bandwidth.capacity, EntityState.scrammed, commissionQueue

65 sim tests, 318 across the repo. Melt still tick 1248, zero brownouts
over 20k.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 20:20:32 +10:00

32 lines
1.5 KiB
TypeScript

/**
* Test/dev helpers for LANE-SIM. Not part of the game loop — nothing in src/main.ts,
* src/render, src/ui or src/screen imports this, and it tree-shakes out of the bundle.
*
* Why it exists: contracts v4 gates every id named by a tech's `unlocks`, and that
* includes the mosh reactor, the software decoder and the subsampler. So the reference
* factory — the endgame demo — is no longer buildable from a fresh sim by design. It
* represents a save where the research has already happened, and this is how a test says
* so honestly, through the public save/load API rather than a back door into the sim.
*
* There is currently no legitimate in-sim path to this state: DATA ships no `kind:'lab'`
* machine, and every science pack is made on the artifact-bottler, which is itself locked
* behind a tech costing 10 analog-packs. See NOTES round 4.
*/
import type { GameData, Sim } from '../contracts';
/**
* Rewrites a sim's research state to treat `techIds` as completed, via a save/load
* round-trip. Call it on a fresh sim before enqueueing anything — load() replaces the
* whole world, so any entities placed first would be discarded.
*/
export function grantResearch(sim: Sim, techIds: string[]): void {
const blob = JSON.parse(sim.save());
blob.research = { active: null, progress: {}, unlocked: [...techIds] };
sim.load(JSON.stringify(blob));
}
/** The late-game save: everything DATA's tech tree gates is open. */
export function grantAllResearch(sim: Sim, data: GameData): void {
grantResearch(sim, data.tech.map((t) => t.id));
}