diff --git a/fktry/lanes/LANE-SIM.md b/fktry/lanes/LANE-SIM.md index 8e9ae52..47fe600 100644 --- a/fktry/lanes/LANE-SIM.md +++ b/fktry/lanes/LANE-SIM.md @@ -73,3 +73,100 @@ CONTRACT REQUEST: (if any) PROPOSAL: (ideas for the orchestrator, optional) NEXT: what you'd do next round if asked ``` + +### Round 1 — 2026-07-17 — Opus 4.8 + +SHIPPED +- The stub is gone. `src/sim/` is now grid + placement, belts with back-pressure, crafting, + bandwidth/brownout, shipping and commissions. Files: `index.ts` (the sim), `geom.ts` + (grid math), `rng.ts` (seeded mulberry32), `sim.test.ts` (18 tests). +- Verified three ways, not one: + 1. `npm run check` clean; `npm test` 70/70 green across all lanes (18 are mine). + 2. Live at localhost:8150 (another session's dev server — see BLOCKED). App boots, zero + console errors. My numbers are visibly driving other lanes: the UI bandwidth readout + showed `9 DRAW / 20 GEN` for an ASIC + extractor + 4 belts + uplink, which is exactly + my power math; the fax panel reads my `activeCommission`/`commissionProgress`. + `__fktryBus.dispatch(...)` places entities and they render. + 3. Ran the sim in the real browser engine by importing `/src/sim/index.ts` in the page: + the M1 chain shipped its first MELT at **tick 1149**, commission `first-taste` closed + at tick 1149 — the identical tick Node produces. Determinism holds across engines. +- Perf (measured in-browser, not estimated), against the 1,000 entity / 5,000 item target: + - 1,245 entities + 2,436 belt items → 0.138 ms/tick (242× realtime) + - 3,500 entities + 6,820 belt items → 0.418 ms/tick (**80× realtime**) + +DECISIONS +- **Output-belt routing by rank — the big judgement call, please review.** A machine picks + which adjacent belt to hand an item to by tracing the belt chain to the machine it ends + at (cached; invalidated on any place/remove/rotate/setRecipe): rank 2 = that machine + consumes this item, 1 = it's an uplink (takes anything), 0 = dead end or loop (accept, so + a half-built belt still fills instead of looking broken), -1 = it will never take this + item, so refuse. It never falls back to a worse rank just because the best belt is + momentarily full. Why I did this: with no splitters or inserters, the demuxer's three + outputs (luma / slurry / mv-flux) have to leave one 2x2 perimeter toward three different + machines. Without ranking, items land on the wrong lane and wedge it permanently — M1 + cannot flow at all. This is routing intelligence the orders didn't ask for; it's also the + root of the deadlock below. If you want it gone, splitters have to land first. +- Belt spacing is enforced *across the tile seam*: a blocked front item holds station 0.45 + behind the rear item of the next belt rather than bunching at the boundary. +- Pause freezes sim time (`tick` stops advancing) but commands still apply — you can build + while halted. `tick` is sim-time, not wall-time. +- `jammed` reports only `'output full'`, edge-triggered (one event per stall, not per tick). + Starvation is not flagged — see PROPOSAL 3. +- Power: a negative recipe bandwidth adds to **gen** rather than subtracting from draw, so + the brownout multiplier stays `gen/draw`. Tested: the quantizer visibly pays for itself. +- Extractors mine anywhere — there is no seam map in `data/` yet. +- Tests assert against the recipe defs instead of hard-coding yields. This is deliberate: + my first pass hard-coded `demux-ore` and broke the moment LANE-DATA shipped. Mechanics are + mine to pin down; the numbers are theirs to move. + +BLOCKED/BROKEN +- **My round-1 definition of done no longer exists.** The orders say place + extractor→demuxer→quantizer→mosh→shipper and watch melt climb. LANE-DATA's codex + transcription landed mid-round and the mosh reactor now eats `gop-crate`, not coefficient + packs. MELT is now **eight machine types** deep (ore→demux→{luma→quantizer→coeff; + slurry→subsampler ×2→chroma-420}→DCT press→anchor slab; mv-flux→p-caster→delta wafers; + →GOP assembler→mosh). I rebuilt against the real graph and it *does* ship melt (tick 1149, + verified above) — but "the whole fantasy in miniature" is now a ~50-entity build, not a + 6-machine demo. Your call whether M1's demo target changes or DATA adds a starter recipe. +- **The M1 factory deadlocks permanently at ~tick 1200**, after 4 GOP crates / 12 melt. + This is not a mechanics bug — I probed it for 6000 ticks. Luma and coefficient packs + structurally overproduce (demux yields 2 luma/craft; the press is chroma-420-limited so it + only burns 6 coeff per ~180 ticks), and the press also jams on surplus anchor slabs (the + GOP assembler needs 1 slab per 6 delta wafers). Surplus has nowhere legal to go: my rank + rule forbids diverting a rank-2 item to a rank-1 uplink even when the preferred belt is + wedged. So the press jams → quantizer jams → demuxer jams → mv-flux stops → the p-caster + starves and the assembler sits at 5/6 delta wafers forever. + - The graph *is* solvable today without new code: a second quantizer on `quantize-crime` + drains surplus coeff into bricks, and a second assembler on `assemble-gop-i-only` eats + surplus slabs (8→crate). My demo build is just naive. But note a player currently + **cannot void surplus to an uplink**, which is the first thing a Factorio player reaches + for. That's the cost of the rank rule. +- `splitter` (`lane-splitter`) and `buffer` (`buffer-tank`) now exist in machines.json; they + place and draw power but do nothing. Not in my orders — flagging so nobody thinks they work. +- `heat` is always 0, `bandwidth.stored` always 0 (M2 / buffer tanks). +- Port 8150 is held by another session's dev server, and the harness won't start a second one + for this project. I verified against that server read-only. Note for whoever plays the + build in a Browser pane: the tab reports `visibilityState: hidden`, so rAF is paused and + the sim crawls (~9 ticks per screenshot). That's the pane, not the loop — importing the sim + module directly in the page runs it at ~96k ticks/sec. + +CONTRACT REQUEST +- None. `contracts.ts` covered everything I needed this round. + +PROPOSAL (yours to call, not mine to take) +1. **Splitters next.** `lane-splitter` with round-robin plus a priority/overflow output is + the canonical Factorio answer to the deadlock above, and it unlocks byproduct sinks and + the bloom-loop topology later. This is the one I'd pick. +2. Alternative, ~5 lines: allow a lower-ranked belt when every preferred belt is full *and* + outputBuf has hit the stall threshold. Cheap, but it makes byproduct management automatic + and quietly deletes a core Factorio tension — a gameplay decision, so I left it alone. +3. `jammed: 'starved'` for idle-with-no-input would let the inspector tell "backed up" from + "waiting". Cheap, but it's UI-visible, so it's LANE-UI's business as much as mine. +4. Extractors should require a seam tile — needs a seam/ore map from DATA and a worldgen call. + +NEXT (round 2 if asked) +- `lane-splitter` with priority/overflow; `buffer-tank` feeding `bandwidth.stored` to smooth + brownouts. +- Heat: accumulate per craft, throttle >0.7, scram at 1 (the software decoder's whole bit). +- Save/load — `SimSnapshot` is already the right shape to serialize. +- A worked, deadlock-free reference factory you can paste into the console to demo M1.