Skip to content

Three Vulcanus lava gates agree by coincidence, and nothing would notice if they came apart #369

Description

@wormeyman

Three separate gates on Vulcanus refuse the same two tiles, lava and
lava-hot. They are equal today. They are not the same rule, and the
repo now says so in one place but has nothing that would notice if they came
apart.

gate where what actually decides it
cliffs tiles/vulcanus_catalog.rs, VulcanusTile::is_cliff_blocking the cliff's collision mask holds water_tile, and tile_collision_masks.lava() sets it
rocks rocks/vulcanus_placement.rs, tile_allowed the four rock prototypes' vulcanus_tiles_cold / vulcanus_tiles_hot autoplace lists, whose union is every Vulcanus tile except these two
geysers resources/vulcanus_geyser.rs, tile_allowed type = "resource" defaults to the collision mask {resource = true}, which tile_collision_masks.lava() also lists

Three unrelated axes in the game's data. Change any one of them and the three
sets come apart.

Why this is a ticket and not a refactor

#364 removed real duplication: the cliff set had four copies and nothing
compared them. The obvious next step is to notice these two gates and fold them
into is_cliff_blocking as well. That would be wrong, and it is worth
having written down somewhere a future audit will look, because the code reads
as three identical matches! arms and the argument against merging them is not
visible from the shape.

A shared predicate would be wrong in two places at once, and silently: the
rock and geyser gates would keep answering the cliff question after the cliff
question stopped being their question.

resources/vulcanus_geyser.rs already carried the finding in its own doc
comment - "the forbidden set coincides with the rock overlay's while being
reached by a completely different route" - which is what caught it. #364 added
the same warning to is_cliff_blocking, with the table above.

What would actually make them diverge

Any of these, all of which are data changes rather than code changes:

  • a new Vulcanus tile whose mask sets water_tile but which the rock
    prototypes' tile lists still allow
  • a rock prototype's vulcanus_tiles_cold / vulcanus_tiles_hot list gaining
    or losing a tile
  • tile_collision_masks.lava() dropping resource = true while keeping
    water_tile, which would split the geyser gate off from the cliff gate while
    leaving the rocks alone

None is hypothetical in kind: the cliff set itself was established by
measurement, not by reading the masks, and the notes on it say a future
blocking tile would be found the same way.

Possible shapes for a fix

Not obviously worth doing yet, which is why this is filed rather than done:

  • Give the rock and geyser gates their own named predicates, each with its own
    comment naming its mechanism, so all three read as three rules instead of
    three copies. Cheap, and it makes the independence visible at the call site.
  • Grade each of the three against the game's own data separately, so a data
    change moves exactly the gates it should. The rock gate already has
    the_tile_gate_refuses_exactly_the_two_lava_tiles; the geyser gate does not
    have an equivalent, and the cliff gate's is now the The Vulcanus cliff-blocking tile set has four definitions and nothing checks they agree #364 parity test.
  • Add the recheck to whatever runs when the pinned Factorio version moves,
    since that is when the data can change under all three at once.

Found while doing #364, by sweeping the repo for Lava | LavaHot rather than
trusting that issue's table, which listed four sites and missed these two.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions