test(cliffs): two LEVERS - no entity suppresses a cliff, lava is 166/169 (#84) - #111
Merged
Conversation
…169 (#84) #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. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015C8AbtvYMdc7ed3rnAZbhP
This was referenced Aug 3, 2026
wormeyman
added a commit
that referenced
this pull request
Aug 3, 2026
…ips (#84) (#112) `cliffErrorBudget.spec.ts`'s constant named `SHIPPED` did not set `rejectAtCrossingStage`, which landed in `renderVulcanusCliffs.ts` with #108. So the file pinned the pre-#108 model - 25 surplus and precision 0.9839 - while the shipping path was at 22 and 0.9858, and it is the file people read for "current accuracy". The port is unchanged; only the measurement was wrong. Corrected figures over the same three oracle regions: | | position only | position AND orientation | | --- | --- | --- | | recall | 0.9961 (1525/1531) | 0.9824 (1504/1531) | | precision | 0.9858 (1525/1547) | 0.9722 (1504/1547) | 1504 matched, 21 wrong orientation, 22 surplus, 6 missing. Also in this file: - `SHIPPED` now carries a comment saying it must mirror the renderer's call, naming this drift as the reason. - The "precision is the dominant defect" guard goes from `x4` to `x3`, because the gap narrowed by #108 removing 3 surplus cells and no missing ones - the improvement working, not a weakened test. - The entity-collision block said item 3 was OPEN and "rocks are the lead". #111 closed it with a lever, so the block is retitled and points there; its two arms are kept as independent corroboration, and the one statistic in them that was measured at 25 surplus is marked as history rather than silently re-quoted. Same treatment for two other stale quotes: the NOTES banner people are told to read before quoting an accuracy number, and a prose figure in `cliffPhantomNeighbour.spec.ts` (whose assertions run with rejections off and do not depend on it). Claude-Session: https://claude.ai/code/session_015C8AbtvYMdc7ed3rnAZbhP Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 3, 2026
Merged
wormeyman
added a commit
that referenced
this pull request
Aug 3, 2026
#84) (#115) #114 reduced the residual to 31 destruction disagreements and handed over one question: Surface::wouldCollide runs constCollideWithTile against the REAL surface while the port resolves tiles from our own Vulcanus model, so a disagreement between the two inside a cliff's box would produce exactly this two-sided error set. It read as needing a new capture. It needed none. The tile half is exonerated for all 6 false rejections. oracle-vulcanus-lava-boundary is a committed 994-position dense capture of surface.get_tile(x, y).name, taken for a different question back when the collision box was the defect, and it covers every tile of all six boxes: 70 of 70 covered, ZERO disagreements, both directions. The game read the same lava we do and placed the cliff anyway. Vacuity arms included, because "0 mismatches" is also what a comparison that never ran would print. A second candidate died in factorio-data rather than in a capture: tiles-vulcanus.lua gives 17 tiles ground() and exactly 2 - lava and lava-hot - lava(), so VULCANUS_CLIFF_BLOCKING_TILES is provably complete. The 25 in the other direction do NOT share a cause. By Chebyshev distance from each box to the nearest tile our own model calls lava, scored against the 1525 cells the port gets right so there is a base rate: within 2 tiles missed 9 (36%) matched 52 (3.4%) 4 to 11 tiles missed 6 matched 436 none within 12 missed 10 matched 1037 The near group is enriched 10.5x - the signature of a boundary or a box a tile short. But ten of the 25 have no lava within twelve tiles, so no adjustment to a lava collision box can reach them; neither can the ore rule (all 25 are ore=false) nor any entity (#111's lever moved zero cliffs). They also cluster where the near group does not. So the handover framing was too narrow: "which cells does wouldCollide reject that ours does not" is the right question for at most 15 of the 25 and for none of the 6. Fitting one box to all 31 would be fitting a rule to two causes at once, which is the failure #88 exists to record. Refuted on the way, and recorded so nobody re-derives it: cliff-versus-cliff collision. Attractive because applyCliffs adds each cliff to the surface right after testing it, and because #111's autoplace_settings lever cannot remove cliffs - so this is the one case that lever never covered. It dies on the base rate: 9 of the far 10 overlap another cliff's rotbb box, and so do 1405 of the 1531 cliffs the game KEEPS (91.8%). Independently, the cliff prototype's generic collision_box is {{-0.99,-0.49},{0.99,0.49}} - "intentionally small" - and cliff cells sit on a 4-tile grid, so generic boxes cannot overlap at all. Nothing shipping changes; this is measurement only. Claude-Session: https://claude.ai/code/session_01KVXnqZPGsD7A2SaPVA9ArA Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
wormeyman
added a commit
that referenced
this pull request
Aug 4, 2026
…#136) #130 proposed a mechanism class new to #84 - entity autoplace ROLLS, so removing calcite shifts the per-chunk RNG stream - and said "nothing here tests it". This is that test. No capture: every fact is in fixtures already committed, one of them since #111. The hypothesis is not refuted. The evidence for it is. 1. **n is ONE crater, not eight.** The eight `crater-cliff` entities are eight segments of a single ring - centre (1646.62, 1679.75), radii 4.95 to 7.00, against the prototype's own `crater_radius = 7`. FFF #386 describes exactly that. Effective sample size 1; #130's table invites reading it as 8. 2. **The spatial reading would have been vacuous.** All eight sit within 0.33 tiles of a calcite entity - and their chunk holds 805 calcite over 1024 tiles, 78.6% coverage. Landing on calcite there is expected. 3. **#130's `geyser OFF` control is VACUOUS.** Zero geysers in the ring's chunk; all 19 are in (48,48), (48,49), (53,54). The lever works globally - removes all 19, moves cliff-vulcanus 885 -> 889 - and cannot touch that chunk's stream by construction. 4. **A lever unrelated to calcite also produces craters**, unread in the #111 fixture: `LAVA TILES OFF only` gives 7 craters with resources still ON, and both levers together give 18 against 8 and 7 alone. 5. **And that row is a data fact.** `crater-cliff` overrides the default cliff mask with `{item, object, player, water_tile}`, and on Vulcanus only lava and lava-hot carry `water_tile`. No RNG needed. Calcite's own route stays open, with two more closed by reading rather than assuming: not entity collision (`resource` mask is `{resource}`, disjoint from every cliff mask including crater-cliff's - #124's argument extended rather than carried over), and not lava tiles (#128 measured zero blocking tiles moved). A third fact from the same file closes a route this session had to consider: the default `cliff` mask carries `not_colliding_with_itself = true`, so cliffs never collide with same-prototype cliffs. That matters because `applyCliffs` adds each cliff to the surface before testing the next, and 57 of 400 adjacent orientation pairs have overlapping boxes. No new fixtures, no shipping behaviour change. Claude-Session: https://claude.ai/code/session_01MmitdaFJxN9Wm8LkQu33kc 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.
#110 handed over "find a lever that isolates the non-ore suppressor the way
autoplace_controlsisolated the ore", with the suspect unnamed. This adds two, throughmap_gen_settings.autoplace_settings- a knobautoplace_controlscannot reach, because a control only touches prototypes that name one, and the rocks, the chimneys andcrater-cliffname none.No placed entity suppresses a Vulcanus cliff
Switching the whole
entitycategory off removes every autoplaced entity in the region - 409 rocks, 115 chimneys, 45 rock explosions and all 8crater-cliffs - and the cliff set does not move by one cell: same 916 cells, same orientations, bit for bit.Rocks were previously refuted only statistically, against the port's own rock model (#109). This is the whole class, with the lever's own proof in the same run three ways:
autoplace_settingsread back off the surface, the 569 entities that vanished, and the crater ring that went with them. The vacuity arm runs the other way too -cliff-vulcanuscomes fromcliff_settings, so it must survive the lever, and it does.crater-cliffis worth a line of its own: a real Vulcanus feature the port does not model, and every spec'sname === "cliff-vulcanus"filter had been discarding it (the #94 trap). It is not the residual - the whole ring is 47 to 180 tiles from the nearest disputed cell.Cliffs do not collide with each other
Refuted by the game's own output rather than by a model: 293 pairs of its own cliffs have overlapping collision rectangles.
The lava lever, and the rejection's first precision/recall
Dropping
lava/lava-hotfrom the tile category leaves the elevation the crossings read untouched and takes away the only thing the tile test can reject against.Its errors are the residual, not a scatter. With 1,058 cells to be wrong about, all 8 land inside the 26 the port already disputes: 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, which a uniformly-too-small or too-big box cannot produce - the shape is wrong, not the size, so it is left measured and unfixed per #88.
The sharpest statement of the residual there has been
[1500,1500], no ore, no lavaRecall 1.0000. The crossing field plus the repair produce a strict superset of the game's cells, so everything left is over-placement, and the two rejections are the only things that can remove one.
What led here
Gap from each cell's collision box to the nearest lava tile, against the matched cells as control:
A 40x enrichment - kept with two cautions: the effective sample size is 4 clusters, not 23 cells, and one cluster has no lava within 10 tiles. The lever confirms that reservation.
pnpm run verifygreen (1399 tests).🤖 Generated with Claude Code
https://claude.ai/code/session_015C8AbtvYMdc7ed3rnAZbhP