feat(oracle): the resource lever moves TILES - confounded, but inert for cliffs (#84) - #128
Merged
Conversation
…for cliffs (#84) Every ore result in #84 uses one instrument: switch a resource off through `map_gen_settings.autoplace_controls` and see which cliffs come back, treated throughout as removing entities and nothing else. It does more, and the game's data files say so: vulcanus_calcite_size = slider_rescale(control:calcite:size, 2) vulcanus_calcite_region = ... vulcanus_calcite_size ..., control:calcite:frequency ... volcanic_jagged_ground_range = 5 * min(10, max(vulcanus_calcite_region + 0.2, ...)) That last one is a TILE range, so `calcite size = 0` changes which tile the argmax picks - a live route from the lever to `Surface::wouldCollide`'s tile half, exactly the kind #124 was left looking for after closing the entity half. Measured against the GAME, three arms over [1500,1500] on a uniform stride-2 grid of 16384 points: | arm | tiles changed | blocking gained | blocking lost | | --- | --- | --- | --- | | calcite OFF | 841 (5.1%) | 0 | 0 | | ALL resources OFF | 1066 | 0 | 0 | Every change is ground-to-ground - jagged-ground losing the argmax to folds, folds-warm, soil-dark, soil-light, folds-flat. Nothing crosses the lava/lava-hot boundary, so `constCollideWithTile` cannot see any of it and EVERY ORE RESULT IN #84 STANDS. But the instrument was mischaracterised, and anyone using `autoplace_controls` for a new question needs to know it moves tiles. Non-vacuity: 1682 of the 16384 sampled tiles ARE blocking, so there was a boundary to cross, and each arm records the controls the SURFACE read back. Our port reproduces it - 3335 of 65536 changed, 0 blocking, a rate of 5.09% against the game's 5.13%. That corroborates the port on a lever nobody had pointed at it, and is what lets its "no blocking flip anywhere" cover the 75% of tiles the grid skipped. Control: tungsten OFF moves zero tiles here, because there is no tungsten in this region. WHERE THIS LEAVES THE MECHANISM - a three-way contradiction, recorded rather than smoothed over. Both halves of `Surface::wouldCollide` are now closed against the ore (entity by #124, tile here), yet #124 also found the ore acting at the destroy stage, where `wouldCollide` is the only thing that destroys. All three cannot be right. Ranked by corroboration, the destroy-stage result is weakest at n=1 - and its one cell, `1546,1550.5`, is also one of the six #125's cascade does not explain, so it has been anomalous twice. Harness: `sampleTileNames` gains an `autoplaceControls` override and a `sampleTileNamesFull` sibling returning the controls the surface read back. New fixture + PROVENANCE entry; capture takes ~7s. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jeznBmWiZywUc7cBw9xFU
wormeyman
added a commit
that referenced
this pull request
Aug 4, 2026
… effect remains (#84) (#129) * test(cliffs): every route from the ore to a cliff is closed - and the effect remains (#84) #128 closed the tile half and recorded a three-way contradiction. Two more routes are now closed, and the honest summary is stronger than "unknown": the search space is EXHAUSTED, not merely unexplored. | route | evidence | | --- | --- | | the cliff FIELD reads a resource | `cliff_elevation = cliff_elevation_from_elevation = elevation = vulcanus_elevation`, whose 47-node expression closure holds no resource region; and our port's raw cell set is BIT-IDENTICAL under every lever arm (2277 cells, same codes) | | `Surface::wouldCollide` does more | disassembled: per-orientation box, degenerate early-out, `constCollideWithTile`, `collideWithEntity`, both with the cliff's own mask at `proto+0x2b0`. No third input | | its ENTITY half | #124 - disjoint masks at prototype level, ordering | | its TILE half | #128 - 841 tiles move, none crosses lava/lava-hot | | the lever perturbs STRUCTURALLY | this PR - richness x2 and x0.5 move ZERO cliffs | The last row was the only route #128 left open. `control:calcite:richness` appears in `vulcanus_calcite_richness` ALONE - not in `vulcanus_calcite_probability` (where the ore lands) nor in `vulcanus_calcite_region` (which drives the volcanic_jagged_ground_range tile) - so it hands the generator a different CompiledMapGenSettings describing the same world. Over [1500,1500]: 861 cliffs and 3933 resources in all three arms, identical cell for cell and orientation for orientation. The surface reads the changed richness back, so the override applied. So the lever is not a structural perturbation: the effect tracks ore PRESENCE - and nothing that can see ore presence can reach a cliff. This does NOT say the rule is wrong. Box overlap against real resource positions reproduces the game at precision 1.000 over 31 cells and survives out of sample (#126). The rule is right; the mechanism is missing, and the port's model of map generation is incomplete in a way none of these five rows covers. Recorded so the next person does not re-walk a closed route. Three genuinely untouched ideas are listed in the notes: chunk-generation ORDER beyond the mask argument, whatever happens to the queue between `generateCliffs` and `applyCliffs`, and `CliffCraterPlacer::tryToPlaceCliffAsCrater` - ruled out for the residual by position but never as a mechanism. New fixture + PROVENANCE entry; capture takes ~7s. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jeznBmWiZywUc7cBw9xFU * test(cliffs): close the cliffiness gap in the field argument (#84) The field-closure row named `cliff_elevation` only. The cliff generator reads TWO properties, and a closure argument that covers one of them leaves the gate unexamined - `planet-map-gen.lua` routes `cliffiness = "cliffiness_basic"` for Vulcanus, and nothing had traced that one. It is clean: `cliffiness_basic`'s closure is a single node referencing only `x`, `y` and `cliff_richness`. So the row stands, but now for both properties rather than for the one that happened to get traced first. Found while checking whether crater-cliff's appearance under the lever shared a cause; `crater_cliff` (47-node closure) and both Vulcanus biomes it reads are clean too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jeznBmWiZywUc7cBw9xFU --------- 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
…#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>
wormeyman
added a commit
that referenced
this pull request
Aug 14, 2026
…ty (#84) (#200) * test(oracle): add a data-stage prototype override hook (#84) Checkpoint, NOT PR-ready - no regression test yet. `autoplaceControls` and `cliffSettings` are surface settings and can be changed at on_init. A prototype field cannot: it is read at map-gen from the loaded prototype, so the only lever is the data stage. That is the difference between "switch the ore off" and "leave the ore exactly where it is and change one property of it", and only the second can separate a resource's PRESENCE from a specific behaviour of it. - `extraDataLua` is written to data-final-fixes.lua, not data.lua. This mod declares no dependencies, so Factorio may run its data.lua before space-age's, at which point data.raw.resource["tungsten-ore"] does not exist and the override would silently do nothing. - The protos dump gains `cliff_removal_probability` and `map_grid`, read back off the running game, so an override arm has to prove it applied. What it bought, on the blob region with COLLAPSED cliff settings: arm blob cliffs resources removal_prob A control 0/10 335 945 1 B removal_prob = 0, ore present 10/10 345 945 0 C resources OFF 10/10 345 0 1 B leaves 945 resource entities exactly where the control has them and the cliffs come back regardless, and B is indistinguishable from C. So `ResourceEntityPrototype::cliff_removal_probability` is the mechanism behind the ore -> cliff exclusion that #99, #100, #110, #128 and #129 characterised empirically and that vulcanusOreRejection.ts still records as "NOT established". It defaults to 1.0 and no shipped prototype overrides it, which is why the port's unconditional box-overlap rejection is correct as written - the behaviour does not change, the explanation does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CEgTMcwvbm67ddUUtudY6X * test(cliffs): name the ore->cliff mechanism - cliff_removal_probability (#84) `vulcanusOreRejection.ts` opened with "NOT established: the mechanism. This is a characterised empirical rule, not a port of a known engine path." It is established now, and the answer was a documented prototype field: `ResourceEntityPrototype::cliff_removal_probability`, default 1.0, overridden by no shipped prototype - so it is invisible from the data and can only be seen by changing it. Five issues of `autoplace_controls` work could not reach it because a surface lever can only add or remove the ore. A prototype lever leaves the ore in place and changes one thing about it: arm blob cells cliff-vulcanus resources field control 0/10 335 945 1 field = 0 10/10 345 945 0 resources OFF 10/10 345 0 1 The middle arm keeps all 945 resource entities exactly where the control has them and the cliffs return regardless, so no account resting on the ore's PRESENCE survives. It is indistinguishable from the resources-OFF arm, and 345 - 335 is exactly the ten blob cells, so the field accounts for the effect entirely rather than partly. Every arm reads the field back off the running game, so an override that failed to apply cannot be mistaken for a term that does not matter. NO BEHAVIOUR CHANGES. At 1.0 the removal is unconditional, so the ported box-overlap rejection is correct exactly as written. What changes is the record: the disassembly finding (cliffs are computed and placed before any resource entity exists) stops being a dead end and becomes the reason the effect had to be a removal, and the thin n=1 destroy-stage result gains outside support - a field that removes cliffs can only act on cliffs that already exist. Still open, and said so in every place touched: the GEOMETRY the engine removes with. The base collision_box remains an empirical fit and the box still must not be tuned. - `OracleOptions.extraDataLua`, written to data-final-fixes.lua rather than data.lua because this probe mod declares no dependencies and may otherwise load before space-age. - The protos dump gains cliff_removal_probability and map_grid. - test/cliffRemovalProbability.spec.ts, its fixture and provenance. Discrimination checked by planting a confused-arm error: exactly the two mechanism tests fail, and the two that read only the control arm correctly do not. - Stale "mechanism is open" text corrected in vulcanusOreRejection.ts, cliffPlacement.ts, cliffOreActsAtDestroyStage.spec.ts and vulcanus-cliffs-NOTES.md. The point-in-time spec under docs/superpowers/specs/ is left alone by convention. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CEgTMcwvbm67ddUUtudY6X --------- 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.
Every ore result in #84 uses one instrument: switch a resource off through
map_gen_settings.autoplace_controlsand see which cliffs come back. It has been treated throughout as removing entities and nothing else. It does more, and the game's data files say so.That last one is a tile range, so
calcite size = 0changes which tile the argmax picks - a live route from the lever toSurface::wouldCollide's TILE half, exactly the kind #124 was left looking for after it closed the entity half.Measured against the game
Three arms over
[1500,1500], uniform stride-2 grid, 16384 points:Every change is ground-to-ground -
volcanic-jagged-groundlosing the argmax to-folds,-folds-warm,-soil-dark,-soil-light,-folds-flat. Onlylavaandlava-hotcarrytile_collision_masks.lava(), and nothing crosses that boundary, soconstCollideWithTilecannot see any of it.Every ore result in #84 stands. But the instrument was mischaracterised, and anyone reaching for
autoplace_controlsfor a new question needs to know it moves tiles.Non-vacuity: 1682 of the 16384 sampled tiles are blocking, so there was a boundary to cross; and each arm records the
autoplace_controlsthe SURFACE read back, so "no tile moved" cannot be confused with "the override never applied".Our port reproduces it, which covers the unsampled tiles
The capture is a 25% sample. Our tile model answers over all 65536 and says 3335 changed, 0 blocking - a rate of 5.09% against the game's 5.13%, on a lever nobody had ever pointed at it. That corroborates the port independently and is what lets its "no blocking flip anywhere" cover the 75% the grid skipped.
Control: tungsten OFF moves zero tiles here - there is no tungsten in this region; it is what fills
[0,0], where #126 measured the ore suppressing zero cliffs.Where this leaves the mechanism - a three-way contradiction
Both halves of
Surface::wouldCollideare now closed against the ore:applyEntitiesskipping rather than destroyingAnd yet #124 also found the ore acting at the destroy stage, where
wouldCollideis the only thing that destroys. All three cannot be right. Recorded rather than smoothed over, ranked by corroboration:Note that one cell,
1546,1550.5, is also one of the six #125's cascade does not explain. It has now been anomalous twice.Harness
sampleTileNamesgains anautoplaceControlsoverride, plus asampleTileNamesFullsibling returning the controls the surface read back (the existing signature is unchanged and now delegates). New fixture is deliberately a uniform grid rather than the positions our port predicts will change, so it does not encode the model it exists to check.PROVENANCE.jsonentry added; capture takes ~7s.Verification
pnpm run verifygreen - 1478 app (up 9) + 13 worker + 3 container tests, 0 warnings, exit 0. Nothing shipping changed.🤖 Generated with Claude Code
https://claude.ai/code/session_015jeznBmWiZywUc7cBw9xFU