Skip to content

Commit 1109bc9

Browse files
wormeymanclaude
andauthored
test(cliffs): the chunk-border gate cannot be scored from any fixture (#84) (#127)
#122 turned `applyCliffs`' fifth-argument test - `updateConnections` runs on the chunk's outer ring and nowhere else - from an inert reading into the thing its destroyed-versus-never-queued verdict rests on, and said scoring it was now worth doing on its own account. This is that attempt. It is negative, and the reason is worth more than the attempt. The test that ought to work: a cliff end pointing at an absent cell is a DANGLING END. If `updateConnections` ran everywhere there could be none; if it runs only on the outer ring, one could survive on a non-border cell. So the game's own output should separate the readings with no new capture. It does not, because there are none at all. Over THIRTEEN arms from all three Vulcanus cliff fixtures - real settings and the collapsed rule, resources on and off, all four regions, 2785 border and 3750 interior cells judged - zero cells have a dangling end, in either population, in any arm. The detector is not asleep: deleting one cell from each arm makes the same code report one immediately, in all thirteen. Why: every removal mechanism preserves connection consistency. A destruction runs `Cliff::onDestroy`, which trims the neighbour's facing end; `updateConnections` trims dangling ends by definition; and the crossing field never emits one. So BOTH readings of the gate predict exactly what the game shows, and no capture of map-generation output can separate them however many regions it covers. Consequence for #122: its conditional stands and cannot be discharged with what is on disk - and not for want of data. Map generation never produces the world that would settle it, because it never truncates a run without the cascade running. Anyone revisiting needs a different KIND of evidence - the disassembly, or a runtime probe that destroys a cliff outside map generation - not another region. Also pins connection consistency of the game's cliff output across every capture, which nothing asserted before. Claude-Session: https://claude.ai/code/session_015jeznBmWiZywUc7cBw9xFU Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 567e316 commit 1109bc9

2 files changed

Lines changed: 296 additions & 0 deletions

File tree

docs/noise/vulcanus-cliffs-NOTES.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3227,3 +3227,61 @@ section above is still measured on `[1500,1500]` alone. A region with calcite in
32273227
it would be the test that could actually break the rule; there is not one in the
32283228
oracle set, and getting one means choosing a new region rather than re-running an
32293229
existing one.
3230+
3231+
## The chunk-border gate CANNOT be scored from any fixture - and why (2026-08-03, #84)
3232+
3233+
#122 turned `applyCliffs`' fifth-argument test - `updateConnections` runs on the
3234+
chunk's outer ring and nowhere else - from an inert reading into the thing its
3235+
whole destroyed-versus-never-queued verdict rests on, and said scoring it was
3236+
"now worth doing on its own account". This is that attempt. It comes back
3237+
negative, and the reason is worth more than the attempt.
3238+
3239+
`test/cliffConnectionConsistency.spec.ts`.
3240+
3241+
### The test that ought to work
3242+
3243+
A cliff end pointing at a cell that is not there is a **dangling end**. If
3244+
`updateConnections` ran on every cell there could be no dangling end anywhere -
3245+
trimming them is what the pass is for. If it runs only on the outer ring, one
3246+
could survive on a NON-border cell. So the game's own output should separate the
3247+
two readings, with no new capture at all.
3248+
3249+
### It does not, because there are no dangling ends at all
3250+
3251+
Over **thirteen arms** from all three Vulcanus cliff fixtures - real settings and
3252+
the collapsed rule, resources on and off, all four regions - across 2785 border
3253+
and 3750 interior cells judged, **zero** cells have a dangling end. Neither
3254+
population, not one arm.
3255+
3256+
The detector is not asleep: deleting one cell from each arm's set makes the same
3257+
code report a dangling end immediately, in all thirteen.
3258+
3259+
### Why - every removal mechanism PRESERVES connection consistency
3260+
3261+
- A destruction runs `Cliff::onDestroy`, which trims the facing end of every
3262+
connected neighbour, so it cannot leave one dangling.
3263+
- `updateConnections` trims dangling ends by definition.
3264+
- And the crossing field never emits one - `cliffConnections.spec.ts` already
3265+
measured the port's own queue as connection-consistent, and this says the same
3266+
of the game's output.
3267+
3268+
So **both readings of the gate predict exactly what the game shows.** No capture
3269+
of map-generation OUTPUT can separate them, however many regions it covers.
3270+
3271+
### What this means for #122
3272+
3273+
Its conditional stands and **cannot be discharged with what is on disk** - and
3274+
not for want of data, which is the useful part. The gate's only observable
3275+
consequence is in a counterfactual: remove a cell the game has, and ask what its
3276+
neighbour keeps. Map generation never produces the world that would settle it,
3277+
because it never truncates a run without the cascade running.
3278+
3279+
Anyone revisiting this needs a different KIND of evidence - the disassembly
3280+
itself, or a runtime probe that destroys a cliff outside map generation - not
3281+
another region. Do not capture more of the same hoping the gate falls out.
3282+
3283+
### A property worth having anyway
3284+
3285+
Connection consistency of the game's cliff output is now pinned across every
3286+
capture, which nothing asserted before. Any future model that emits a dangling
3287+
end is wrong on the game's own terms, wherever it emits one.
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
import { describe, expect, it } from "vite-plus/test";
2+
3+
import entities from "./fixtures/oracle-vulcanus-cliff-entities.seed123456.json";
4+
import oreDirection from "./fixtures/oracle-vulcanus-cliff-ore-direction.seed123456.json";
5+
import oreRegions from "./fixtures/oracle-vulcanus-cliff-ore-direction-regions.seed123456.json";
6+
import { CLIFF_ORIENTATION_NAMES } from "../src/noise/cliffs/cliffCatalog";
7+
import {
8+
connectedSides,
9+
isCliffConnected,
10+
onChunkBorder,
11+
} from "../src/noise/cliffs/cliffConnections";
12+
13+
/**
14+
* **The chunk-border gate cannot be scored from ANY committed fixture, and this
15+
* is why** (#84).
16+
*
17+
* #122 turned `applyCliffs`' fifth-argument test - `updateConnections` runs on
18+
* the chunk's outer ring and nowhere else - from an inert reading into the thing
19+
* its whole destroyed-versus-never-queued verdict depends on, and said scoring
20+
* it was "now worth doing on its own account". This is that attempt, and it
21+
* comes back negative in a way worth writing down so the route is not retried.
22+
*
23+
* **The test that ought to work.** A cliff end pointing at a cell that is not
24+
* there is a *dangling end*. If `updateConnections` ran on every cell there
25+
* could be no dangling end anywhere, because the pass exists precisely to trim
26+
* them. If it runs only on the outer ring, a dangling end could survive on a
27+
* NON-border cell. So the game's own output should separate the two readings.
28+
*
29+
* **It does not, because there are no dangling ends at all.** Over thirteen arms
30+
* from all three fixtures - every Vulcanus cliff capture on disk, at real settings
31+
* and at the collapsed rule, with the resources on and off - **zero** cells have
32+
* one, on the border or off it.
33+
*
34+
* That is not a null result about the gate; it identifies the reason the gate is
35+
* unobservable. Every mechanism that can remove a cliff during map generation
36+
* **preserves connection consistency**:
37+
*
38+
* - a destruction runs `Cliff::onDestroy`, which trims the facing end of every
39+
* connected neighbour, so it cannot leave one dangling;
40+
* - `updateConnections` trims dangling ends by definition;
41+
* - and the crossing field never emits one to begin with - `cliffConnections.spec.ts`
42+
* measures the port's own queue as already connection-consistent.
43+
*
44+
* So both readings of the gate predict exactly what the game shows, and no
45+
* capture of map-generation OUTPUT can tell them apart. The gate's only
46+
* observable consequence is in a counterfactual - remove a cell that the game
47+
* has, and ask what its neighbour keeps - which is what #122 measures and why
48+
* that result is conditional. **The conditional cannot be discharged with what
49+
* is on disk, and it is not a matter of capturing more of the same.**
50+
*
51+
* What would settle it is a world where a cliff run is truncated without the
52+
* cascade running, which map generation never produces. Anyone revisiting this
53+
* needs a different kind of evidence - the disassembly itself, or a runtime
54+
* probe - not another region.
55+
*
56+
* The zero is also worth having on its own: it pins **connection consistency of
57+
* the game's cliff output** as a property, across every capture, which nothing
58+
* asserted before.
59+
*/
60+
61+
const nameToId = new Map(CLIFF_ORIENTATION_NAMES.map((n, i) => [n, i]));
62+
const K = (x: number, y: number): string => `${String(x)},${String(y)}`;
63+
const SIDE_STEP: readonly (readonly [number, number])[] = [
64+
[0, -4],
65+
[4, 0],
66+
[0, 4],
67+
[-4, 0],
68+
];
69+
70+
interface Ent {
71+
x: number;
72+
y: number;
73+
name: string;
74+
orientation?: string;
75+
}
76+
interface Region {
77+
x0: number;
78+
y0: number;
79+
x1: number;
80+
y1: number;
81+
}
82+
interface Arm {
83+
label: string;
84+
region: Region;
85+
cliffs: Ent[];
86+
}
87+
88+
/** Every Vulcanus cliff capture on disk, as one list of arms. */
89+
const ARMS: Arm[] = [
90+
...(entities.cases as unknown as { region: Region; cliffs: Ent[] }[]).map((c, i) => ({
91+
label: `entities region ${String(i)}`,
92+
region: c.region,
93+
cliffs: c.cliffs,
94+
})),
95+
...(oreDirection.cases as unknown as Arm[]).map((c) => ({
96+
label: `ore-direction: ${c.label}`,
97+
region: c.region,
98+
cliffs: c.cliffs,
99+
})),
100+
...(oreRegions.cases as unknown as Arm[]).map((c) => ({
101+
label: `ore-regions: ${c.label}`,
102+
region: c.region,
103+
cliffs: c.cliffs,
104+
})),
105+
];
106+
107+
interface Tally {
108+
label: string;
109+
border: number;
110+
interior: number;
111+
danglingOnBorder: number;
112+
danglingOnInterior: number;
113+
}
114+
115+
const tally = (arm: Arm): Tally => {
116+
const r = arm.region;
117+
const inR = (p: { x: number; y: number }): boolean =>
118+
p.x >= r.x0 && p.x < r.x1 && p.y >= r.y0 && p.y < r.y1;
119+
const game = new Map<string, number>();
120+
for (const e of arm.cliffs)
121+
if (e.name === "cliff-vulcanus" && inR(e)) {
122+
const id = nameToId.get(e.orientation ?? "");
123+
if (id !== undefined) game.set(K(e.x, e.y), id);
124+
}
125+
126+
const out: Tally = {
127+
label: arm.label,
128+
border: 0,
129+
interior: 0,
130+
danglingOnBorder: 0,
131+
danglingOnInterior: 0,
132+
};
133+
for (const [k, o] of game) {
134+
const [xs, ys] = k.split(",");
135+
const x = Number(xs);
136+
const y = Number(ys);
137+
// Only judge cells whose four neighbours are all inside the queried box, so
138+
// "the neighbour is missing" can never mean "nobody asked for it" - the halo
139+
// artifact `applyCliffConnections` warns about.
140+
if (!(x - 4 >= r.x0 && x + 4 < r.x1 && y - 4 >= r.y0 && y + 4 < r.y1)) continue;
141+
const border = onChunkBorder(x, y);
142+
if (border) out.border++;
143+
else out.interior++;
144+
let dangles = false;
145+
for (const s of connectedSides(o)) {
146+
const [dx, dy] = SIDE_STEP[s];
147+
const n = game.get(K(x + dx, y + dy));
148+
if (n === undefined || !isCliffConnected(s, o, n)) dangles = true;
149+
}
150+
if (!dangles) continue;
151+
if (border) out.danglingOnBorder++;
152+
else out.danglingOnInterior++;
153+
}
154+
return out;
155+
};
156+
157+
const TALLIES = ARMS.map(tally);
158+
159+
describe("the game's cliff output is connection-consistent everywhere", () => {
160+
/**
161+
* The sample, stated first so the zeros below are not mistaken for an empty
162+
* loop: thirteen arms, and both populations are well represented in each - the
163+
* gate's domain is not some rare corner.
164+
*/
165+
it("judges 6535 cells across thirteen arms, both populations present", () => {
166+
expect(TALLIES.length).toBe(13);
167+
const border = TALLIES.reduce((n, t) => n + t.border, 0);
168+
const interior = TALLIES.reduce((n, t) => n + t.interior, 0);
169+
expect(border).toBe(2785);
170+
expect(interior).toBe(3750);
171+
// Every arm has some of each, so no arm is vacuous on its own.
172+
expect(TALLIES.every((t) => t.border > 0 && t.interior > 0)).toBe(true);
173+
}, 300000);
174+
175+
/**
176+
* **Zero dangling ends, on either population, in every arm.** So the two
177+
* readings of the gate - "outer ring only" and "every cell" - predict the same
178+
* output, and no capture of map-generation output can separate them.
179+
*/
180+
it("finds no dangling end anywhere, on the border or off it", () => {
181+
expect(TALLIES.filter((t) => t.danglingOnBorder > 0).map((t) => t.label)).toEqual([]);
182+
expect(TALLIES.filter((t) => t.danglingOnInterior > 0).map((t) => t.label)).toEqual([]);
183+
}, 300000);
184+
185+
/**
186+
* **The non-vacuity arm.** A zero is also what a detector that never fires
187+
* would print, so plant one: take each arm's cell set, delete a cell that has
188+
* a connected neighbour, and confirm the same code then reports a dangling end
189+
* at that neighbour. It does, in every arm.
190+
*/
191+
it("reports a dangling end as soon as one is planted", () => {
192+
let planted = 0;
193+
for (const arm of ARMS) {
194+
const r = arm.region;
195+
const inR = (p: { x: number; y: number }): boolean =>
196+
p.x >= r.x0 && p.x < r.x1 && p.y >= r.y0 && p.y < r.y1;
197+
const game = new Map<string, number>();
198+
for (const e of arm.cliffs)
199+
if (e.name === "cliff-vulcanus" && inR(e)) {
200+
const id = nameToId.get(e.orientation ?? "");
201+
if (id !== undefined) game.set(K(e.x, e.y), id);
202+
}
203+
// Find any connected pair and delete one of them.
204+
let victim: string | undefined;
205+
for (const [k, o] of game) {
206+
const [xs, ys] = k.split(",");
207+
const x = Number(xs);
208+
const y = Number(ys);
209+
for (const s of connectedSides(o)) {
210+
const [dx, dy] = SIDE_STEP[s];
211+
const nk = K(x + dx, y + dy);
212+
const n = game.get(nk);
213+
if (n !== undefined && isCliffConnected(s, o, n)) victim = nk;
214+
if (victim !== undefined) break;
215+
}
216+
if (victim !== undefined) break;
217+
}
218+
expect(victim).toBeDefined();
219+
if (victim === undefined) continue;
220+
game.delete(victim);
221+
222+
let dangling = 0;
223+
for (const [k, o] of game) {
224+
const [xs, ys] = k.split(",");
225+
const x = Number(xs);
226+
const y = Number(ys);
227+
for (const s of connectedSides(o)) {
228+
const [dx, dy] = SIDE_STEP[s];
229+
const n = game.get(K(x + dx, y + dy));
230+
if (n === undefined || !isCliffConnected(s, o, n)) dangling++;
231+
}
232+
}
233+
expect(dangling).toBeGreaterThan(0);
234+
planted++;
235+
}
236+
expect(planted).toBe(13);
237+
}, 300000);
238+
});

0 commit comments

Comments
 (0)