test(cliffs): score the ore rule against the LEVER, and refute the cascade (#84) - #110
Merged
Conversation
…scade (#84) `vulcanusOreRejection.ts` left an explicit open question: the run remainders are "a cascade along cliff connections OR a wider box". #108 makes the cascade half concrete - a rejection zeroes the cell's edge registers, so a neighbour's code, hence its ORIENTATION, hence its collision box, changes - so re-testing to a fixpoint IS that cascade. `rejectionCascades` is the arm. **Refuted.** Bit-for-bit identical at the shipping settings, and on the collapsed rule it loses 14 matched cells and 7 orientations to gain 4 of the over-placement (18654/693/1200/103 -> 18640/700/1196/110). The collapsed-rule row is what makes the shipping no-op a result rather than an untriggered branch. A rejected cell never turns a neighbour into a rejectable orientation. **The crossing STAGE explains 2 remainders for free.** The predicate fires on 20 placed cells; the placement loses 22, because zeroing a rejected cell's edges leaves two neighbours with codes that no longer place. No tuning - it falls out of #108's mechanism, and it is the first thing to reduce the remainder count since the rule was characterised. **And the rule now has a precision and a recall, not a total.** `oracle-vulcanus-cliff-ore-direction` re-ran the region with resources off through `autoplace_controls`, so the ore's effect is a known SET: 31 cells suppressed, 0 appeared (the one-way property of #99, re-confirmed on the entity region), 5 re-coded. Our model suppresses 22, **all 22 correct** - **precision 1.000, recall 0.710**. Exactly right where it fires, simply too narrow, which says which direction is safe to move in. Attribution from the per-control arms: 27 calcite, 4 geyser, 0 tungsten/coal. Of the 9 misses, 4 are geyser cells `includeGeyser: false` deliberately excludes, 5 are calcite, and all 9 are adjacent to another suppressed cell. **Half of [1500,1500]'s residual is not ore at all.** Running BOTH sides with resources off: 876 matched, **13 wrong, 10 surplus**, 3 missing - against 842/16/19/3 with them on. Tuning the ore rule cannot reach that half, and it is the larger target now. What stays open is the wider-box half - the one #88 says must not be tuned into fitting. Item of #84; the issue stays open. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ExK3ASs5i2KMm9NwWqnZuj
This was referenced Aug 3, 2026
wormeyman
added a commit
that referenced
this pull request
Aug 3, 2026
…169 (#84) (#111) #110 asked for a lever that isolates the non-ore suppressor the way `autoplace_controls` isolated the ore, with the suspect unnamed. This adds two, through `map_gen_settings.autoplace_settings` - a knob `autoplace_controls` cannot reach, since a control only touches prototypes that name one and the rocks, chimneys and `crater-cliff` name none. **No placed entity suppresses a Vulcanus cliff.** Switching the whole `entity` category off removes 409 rocks, 115 chimneys, 45 rock explosions and all 8 `crater-cliff`s, and the cliff set does not move by one cell. Rocks were previously refuted only statistically against our own rock model (#109); this is the class excluded positively, with the lever's own proof in the same run three ways. **Cliffs do not collide with each other**, refuted by the game's own output rather than a model: 293 pairs of its own cliffs have overlapping collision rectangles. **Lava suppresses 169 cells and our rejection gets 166, with 5 false positives** - precision 0.9708, recall 0.9822, the first real score that rule has had. Its errors ARE the residual: the 3 it misses are 3 of the 10 surplus cells and the 5 it invents include all 3 of the port's missing ones. They point both ways, so the box's shape is wrong rather than its size - left measured and unfixed, per #88. **With neither ore nor lava, recall is 1.0000** (1049 matched, 9 wrong, 12 surplus, 0 missing of 1058). The port produces a strict superset of the game's cells, so everything left is over-placement and the question is now what else the game refuses. Claude-Session: https://claude.ai/code/session_015C8AbtvYMdc7ed3rnAZbhP Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 4, 2026
wormeyman
added a commit
that referenced
this pull request
Aug 4, 2026
#84) (#125) `vulcanusOreRejection.ts` records box overlap as explaining 21 of the 31 cells the ore suppresses, the other ten as "run remainders", and the cascade half of that question as REFUTED. That refutation is about a DIFFERENT cascade. It tested #108's crossing-stage mechanism - a rejection zeroes the cell's edge registers, a neighbour's code changes, re-test to a fixpoint - and its conclusion ("rejected cells do not turn neighbours rejectable") still stands. The `applyCliffs` cascade is `Cliff::onDestroy` taking the facing end of every connected neighbour and destroying a neighbour left with no end at all. It was read out of the binary in #113, AFTER #110 ran, and nothing had re-run the remainder question against it. Replayed entirely on the game's own data - its ore-off cliff set (892 with the game's orientations), its resource entity positions, the prototype boxes the fixture itself carries - so none of the port's field, ore model or geyser roll appears in the measurement: | arm | matched | wrong | surplus | | --- | --- | --- | --- | | control - destroy the lever's own 31 | 861 | 0 | 0 | | direct overlap only, no cascade | 856 | 5 | 10 | | direct overlap + onDestroy CASCADE | 859 | 2 | 6 | - Direct overlap fires on 21 and every one is in the lever's 31 - precision 1.000 re-derived with no data of ours. - The cascade closes 4 of the 10 remainders and 3 of the 5 orientation errors. Recall 21/31 = 0.677 -> 25/31 = 0.806, no new parameter, no wider box. - `missing` is 0 in both arms, so no cell the game kept is ever removed. That is what separates a mechanism from a bigger rectangle - and #124 established the box is not the engine's collision test to begin with. The control validates the harness, and getting it exact needs `noUpdateConnections: true`: the game's dumped set is POST-pipeline, so running `updateConnections` over it again double-applies the pass and scores wrong=13, which reads like a defect in the cascade model and is an artifact of the harness. Any experiment starting from a dumped set has the same hazard. Six remain: 1546,1550.5 1546,1554.5 1606,1590.5 1606,1594.5 1622,1614.5 1626,1614.5. Two are the geyser pair, and the first is the one cell in this whole residual whose destruction the game's own orientations directly witness (#124's n=1) - so the best-evidenced destruction is still unexplained. Measurement only - the renderer rejects at the crossing stage and does not run `applyCliffConnections` at all. Claude-Session: https://claude.ai/code/session_015jeznBmWiZywUc7cBw9xFU Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
wormeyman
added a commit
that referenced
this pull request
Aug 4, 2026
…count is 14 (#84) (#126) Everything the last three PRs established about the ore rule was measured on [1500,1500], because that is the only region `oracle-vulcanus-cliff-ore-direction` re-runs with the resources off. A rule characterised on one region and never tested on another is fitted until proven otherwise, so the lever was captured for the two regions the entities fixture covers and it never did. | region | resources | ON | OFF | suppressed | | --- | --- | --- | --- | --- | | [0,0] | 945 tungsten-ore | 283 | 283 | 0 | | [-1200,800] | 1047 coal | 387 | 387 | 0 | - PRECISION 1.000 SURVIVES. Our predicate fires on zero cells in both regions, so 1992 resource entities across two fresh regions produce no false positive. This is the arm that would have caught a rule fitted to [1500,1500]. - THE UNEXPLAINED POPULATION IS 14, NOT 11. #123 could only park `106,26.5`, `90,38.5` and `-1050,1022.5` as undetermined because the lever's region did not cover them. It does now, and the ore suppresses nothing there. - Re-confirms #110's attribution - 27 calcite, 4 geyser, 0 tungsten and coal - at far larger scale than the arm that produced it. The non-vacuity check is in the fixture rather than argued: "0 suppressed" is also what a lever that never reached the generator prints, and the OFF arms read back 0 resources against 945 and 1047. What this does NOT establish: neither region contains calcite or a geyser, the only two controls that suppress anything. So this is a PRECISION test, not a recall test - the rule's positive evidence is still one region, and #125's cascade result is still measured on [1500,1500] alone. Breaking the rule would need a region with calcite in it, and the oracle set has none. New fixture is deliberately separate from `oracle-vulcanus-cliff-ore-direction` so regenerating it cannot rewrite ground truth four merged PRs depend on. PROVENANCE entry added. Claude-Session: https://claude.ai/code/session_015jeznBmWiZywUc7cBw9xFU Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
vulcanusOreRejection.tscarried an explicit open question about the "run remainders": a cascade along cliff connections, or a wider box. #108 makes the cascade half concrete and testable - a rejection zeroes the cell's edge registers, so a neighbour's code, hence its ORIENTATION, hence its collision box, changes. Re-testing to a fixpoint is that cascade.rejectionCascadesis the arm.The cascade is refuted
At the shipping settings it is bit-for-bit identical; the collapsed-rule row is what makes that a result rather than an untriggered branch. A rejected cell never turns a neighbour into a rejectable orientation.
The crossing stage explains 2 remainders for free
The predicate fires on 20 placed cells; the placement loses 22, because zeroing a rejected cell's edges leaves two neighbours with codes that no longer place. No tuning - it falls out of #108's mechanism, and it is the first thing to reduce the remainder count since the rule was characterised.
The rule now has a precision and a recall, not a total
oracle-vulcanus-cliff-ore-directionre-ran[1500,1500]with the resources switched off throughautoplace_controls, so the ore's effect is a known set of cells."Exactly right where it fires, simply too narrow" is a far more useful statement than "explains 21 of 31" - it says which direction is safe to move in.
Attribution from the per-control arms: 27 calcite, 4 geyser, 0 tungsten/coal. Of the 9 misses, 4 are geyser cells that
includeGeyser: falsedeliberately excludes (already measured as harmful to include - the geyser rolls), 5 are calcite, and all 9 are adjacent to another suppressed cell.Half of
[1500,1500]'s residual is not ore at all13 wrong orientations and 10 surplus cells survive with the ore entirely out of the picture. Tuning the ore rule cannot reach them, and that non-ore half is the larger target now.
What stays open
The wider-box half - which is the one #88 says must not be tuned into fitting.
rejectionCascadesstays in the tree as a measurement lever, defaulted off, in the same spirit asfixImpossibleCellsandincludeGeyser.Item of #84; the issue stays open.
🤖 Generated with Claude Code
https://claude.ai/code/session_01ExK3ASs5i2KMm9NwWqnZuj