diff --git a/compiler/backend/emit.ts b/compiler/backend/emit.ts index ab41e52..bb94ee9 100644 --- a/compiler/backend/emit.ts +++ b/compiler/backend/emit.ts @@ -878,10 +878,7 @@ class Scope { if (!of || !nativeArray(of) || of.slice || !this.ok(a.slice.offset)) return null; return { array: of, index: merged(index.c, [...index.terms, { id: a.slice.offset, k: 1 }]), jump: true, guard: index, limit: a.length, ...(safe ? { safe } : {}) }; } - if (a.values) { - const bits = a.values.reduce((m, v) => (m | (v >>> 0)) >>> 0, 0).toString(2).replace(/0/g, "").length; - if (bits <= TABLE_BITS) return { array: a, index }; - } + if (a.values && readInPlace(a.values)) return { array: a, index }; return { array: a, index, jump: true, ...(safe ? { safe } : {}) }; } @@ -1148,6 +1145,8 @@ const PAD = 0x0d; * bit, each 13 ns when the bit is clear and 23 when set, against about 200 ns for the Python side's read of a cell. */ const TABLE_BITS = 6; +/** The longest list known when built that is kept as cell triggers whatever its values (a longer one is read in place). */ +const TABLE_CELLS_MOST = 16384; /** The most cells a map of our own's stack has: a trigger of 72 bytes each (4.7 MB at this). */ const STACK_CELLS_MOST = 65536; @@ -1163,10 +1162,20 @@ function inRegion(arrays: Map, id: string): boolean { const of = arrays.get(a.slice.of); return !!of && nativeArray(of) && !of.slice; } - if (a.values) return a.values.reduce((m, v) => (m | (v >>> 0)) >>> 0, 0).toString(2).replace(/0/g, "").length > TABLE_BITS; + if (a.values) return !readInPlace(a.values); return true; } +/** + * A list known when the map is built that is read a bit at a time where it is, not kept as a region of cell triggers: + * one whose values have few bits between them, or one too long to keep as cells — a video's brushes for the fog, + * hundreds of thousands of words, would be tens of megabytes of cells and pass the cells a map lays out; in place it is + * a dword a value, and a read is a trigger a bit the values have (Bad Apple's: 18). + */ +function readInPlace(values: readonly number[]): boolean { + return values.length > TABLE_CELLS_MOST || values.reduce((m, v) => (m | (v >>> 0)) >>> 0, 0).toString(2).replace(/0/g, "").length <= TABLE_BITS; +} + /** * An array a map of our own keeps in a region of cell triggers (or reads where it is: a list of few bits): of numbers or * booleans, one for everyone — not one that grows, holds texts or is a task's. A window on one (a row of a grid) is the diff --git a/docs/v4-plan.md b/docs/v4-plan.md index 311039d..d20eca0 100644 --- a/docs/v4-plan.md +++ b/docs/v4-plan.md @@ -5675,7 +5675,7 @@ and `sweep3.md` of 2026-10-23, 119 scripts) comes to: | Built both ways, lines differ because the game is random | tetris, snake (the board drawn from random pieces: played by hand, the pack) | | Built both ways, eudplib's lines wrong | own-fields ("C: a unit count WRONG" from eudplib, ok from ours), key-held (eudplib prints garbage where ours prints "taps 0 let go 0") | | Refused by the own build | leftovers (a function that calls itself and gives back a text), strings (a unit's name written from a made text), game-closures and game-references (the length of the texts' address array of records kept in an array that grows) | -| Not built here | bad-apple, halftone, sound-probe (their video, image and base map are not on this disk) | +| Not built here | bad-apple, halftone, sound-probe (their video, image and base map are not on this disk). Built after 4.0 (2026-10-25): bad-apple was **refused** — its 657,477 brush words, 18 bits wide, laid out as cell triggers past the 65,536 — and is built since a list longer than 16,384 is read where it is (`readInPlace`), all 5,216 frames equal their cover in the lifted game, game-check clean; halftone builds | | `game-*.ts` tests in the game on the own build, all passed | arrays-cost 7, async 3, clamps 2, cursors 4, emitter 10, errors 7, fdiv 7, fixed 6, function-values, generators 3, location-stale 2, math 4, program 3, ranges 5, short-circuit 1, small-gaps, stdlib-core 5, tasks 4 (after the fix above), thresholds 2, ui 1, unit-order 3, values 3, unit-pixels 1 | | `game-*.ts` tests failing in the game on the own build | events, events-2, events-input, key-held, key-tap, local, local-players, players, tests, raycaster, cutscene, lowering — the harness's events and input on a map of our own (the Open list), to be looked at one by one in step 17; text-arrays past the lifted game's 64 KB of strings | diff --git a/probes/bad-apple.ts b/probes/bad-apple.ts index 56c1f3f..c5531a8 100644 --- a/probes/bad-apple.ts +++ b/probes/bad-apple.ts @@ -1,35 +1,57 @@ -// Bad Apple!! in the fog of war (TrigScript 4, S4 in TrigScript: B8). The video is imported like a file of the -// script — a .video, the frames the editor decoded — and the fog library plays it on the minimap a frame of it every -// frame of the game, with the song from the map's archive. Host it as a multiplayer game, alone, with Classic graphics. +// Bad Apple!! in the fog of war: the TrigScript showcase map. The video is imported like a file of the script — a +// .video, the frames the editor decoded — and the fog library plays it on the minimap, a frame of it every frame of +// the game, with the song from the map's archive. Host it as a multiplayer game, alone, with Classic graphics. // -// The video and the song are not ours and never committed. Made with: +// The map is 128 × 128 Badlands with no units. Its top 96 rows (the board, 4:3) are all tile 23778, white when lit +// and grey in fog on the Classic minimap; the rows below are never seen, so they stay black around the picture, and +// the camera waits there. The video and the song are not ours and never committed. Made with: // npx tsx scripts/import-video.mts