From de6d2c1b33b84aa48d9e02307e8a784aec5b61cf Mon Sep 17 00:00:00 2001 From: 44r0n7 <44r0n7+gitea@pm.me> Date: Thu, 29 Oct 2026 22:06:33 -0400 Subject: [PATCH] No per-frame allocations in the 'doesn't connect' marks (t87) --- docs/PROJECT_MAP.md | 4 ++-- docs/agent-log.md | 11 +++++++++++ src/sim/joins.js | 17 +++++++++++++---- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/PROJECT_MAP.md b/docs/PROJECT_MAP.md index 381eec1..0d89670 100644 --- a/docs/PROJECT_MAP.md +++ b/docs/PROJECT_MAP.md @@ -288,10 +288,10 @@ Pipes, pumps and outlets: each joined line holds one liquid as a count, not as g Per-tick machine work: steam engines, fans, belts, filters, screw conveyors (machinesTick). - functions: engineIntake, fanBlow, machinesTick -### src/sim/joins.js (125 lines) +### src/sim/joins.js (134 lines) Travel-directed transport ends. Precomputed probes; world/plan queries allocate nothing. End indices stay geometric: left/right, or up/down for vertical screws. - functions: pieceEnds, joinCandidate, endJoin, pieceJoins, endAt, joinIsDrop, joinSnap, nearMiss -- variables: JOIN_ENDS, NO_PIECE +- variables: JOIN_ENDS, NO_PIECE, NEAR_MISS_VIEW ### src/sim/conveyors.js (213 lines) How belts, filters and screw conveyors move what is on or in them. What rests on a belt or filter rides along: the whole stack standing on the carrying surface steps one cell toward d together (belts run front of the line first, so the way ahead is clear). If a powder grain is blocked, the load above it stands still and the belt slips underneath. A filter's own material drops through it instead, unless it is already full. diff --git a/docs/agent-log.md b/docs/agent-log.md index f83a4ec..5956be2 100644 --- a/docs/agent-log.md +++ b/docs/agent-log.md @@ -4,6 +4,17 @@ What agents have done, newest first. Add an entry for every piece of work: the d note or request it was for, what changed, the files touched, the tests run, and anything left open or not verified. Keep each entry short; the git history has the details. +## 2026-10-29: t87 reuse the near-miss view (Codex) + +- Reused one scratch view, its entry array and get/has methods in src/sim/joins.js; + replaced the two-row temporary array with a numeric loop. Connection rules unchanged. +- Files: src/sim/joins.js, docs/PROJECT_MAP.md, tasks/t87* and this log. Map regenerated. +- All 41 headless suites pass; every suite's before/after output matches excluding timings + (/tmp/t87-determinism.diff empty). Pixel test/play.py passes with no console errors. + Frozen shots/t87-{before,after}.png match exactly; inspected the after image and red marks. +- test/t_smooth.py fails elbow/collar and ghost-preview image checks on both original and + refactored code, with no page errors; left those existing failures outside t87's scope. + ## 2026-10-29: t83 remove old-save conversions (Codex) - Removed only spring, solid-pipe, single-material-filter and foreground-tree conversions diff --git a/src/sim/joins.js b/src/sim/joins.js index 8ca3aa1..5b7a190 100644 --- a/src/sim/joins.js +++ b/src/sim/joins.js @@ -108,16 +108,25 @@ function joinSnap(code, v, cells) { // A drop join counts as connected. The moved block is tried through a small override view of // `planned`, so ghost maps are never copied. const NO_PIECE = [0, 0]; +// Synchronous join queries never retain this scratch view or call nearMiss recursively. +const NEAR_MISS_VIEW = { + source: -1, moved: -1, planned: null, entry: [0, 0], + get(k) { + return k === this.source ? NO_PIECE : k === this.moved ? this.entry : this.planned ? this.planned.get(k) : undefined; + }, + has(k) { return this.get(k) !== undefined; } +}; function nearMiss(bx, by, end, planned = null) { const b = by * BW + bx, p = planned && planned.get(b), code = p ? p[0] : blockP[b], v = p ? p[1] : blockV[b]; const own = pieceEnds(code, v); if (!own[end] || endJoin(bx, by, end, planned) >= 0) return 0; - const entry = [code, v]; - for (const dy of [-1, 1]) { + const view = NEAR_MISS_VIEW; + view.source = b; view.planned = planned; + view.entry[0] = code; view.entry[1] = v; + for (let dy = -1; dy <= 1; dy += 2) { const y = by + dy, nb = y * BW + bx; if (y < 0 || y >= BH || blockP[nb] || (planned && planned.get(nb))) continue; - const view = { get: k => k === b ? NO_PIECE : k === nb ? entry : planned ? planned.get(k) : undefined }; - view.has = k => view.get(k) !== undefined; + view.moved = nb; const j = endJoin(bx, y, end, view); if (j >= 0 && !joinIsDrop(bx, y, end, j, view) && (own[end].diagonal || endAt(j >> 1, j & 1, view).diagonal)) return dy; }