No per-frame allocations in the 'doesn't connect' marks (t87)

This commit is contained in:
2026-10-29 22:06:33 -04:00
parent f88035a58f
commit de6d2c1b33
3 changed files with 26 additions and 6 deletions
+2 -2
View File
@@ -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). Per-tick machine work: steam engines, fans, belts, filters, screw conveyors (machinesTick).
- functions: engineIntake, fanBlow, 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. 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 - 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) ### 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. 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.
+11
View File
@@ -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 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. 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) ## 2026-10-29: t83 remove old-save conversions (Codex)
- Removed only spring, solid-pipe, single-material-filter and foreground-tree conversions - Removed only spring, solid-pipe, single-material-filter and foreground-tree conversions
+13 -4
View File
@@ -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 // A drop join counts as connected. The moved block is tried through a small override view of
// `planned`, so ghost maps are never copied. // `planned`, so ghost maps are never copied.
const NO_PIECE = [0, 0]; 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) { 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 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); const own = pieceEnds(code, v);
if (!own[end] || endJoin(bx, by, end, planned) >= 0) return 0; if (!own[end] || endJoin(bx, by, end, planned) >= 0) return 0;
const entry = [code, v]; const view = NEAR_MISS_VIEW;
for (const dy of [-1, 1]) { 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; const y = by + dy, nb = y * BW + bx;
if (y < 0 || y >= BH || blockP[nb] || (planned && planned.get(nb))) continue; 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.moved = nb;
view.has = k => view.get(k) !== undefined;
const j = endJoin(bx, y, end, view); 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; if (j >= 0 && !joinIsDrop(bx, y, end, j, view) && (own[end].diagonal || endAt(j >> 1, j & 1, view).diagonal)) return dy;
} }