Skip to content

Cross-overlay occupancy: earlier autoplacers block later ones (enemy bases are 47% over in region 0) #16

Description

@wormeyman

Successor to #9. The placement roll now covers all five overlays, and the density
agreement is good everywhere except one region, where the cause is measured, not
suspected
: entities that placed earlier occupy the ground later ones would use.

The evidence

Nauvis enemy bases, region [0,0]-[512,512], against
test/fixtures/oracle-entity-counts.seed123456.json (2.1.12, seed 123456):

model placed game
roll + tile gate + own-overlay collision (shipped) 28 19
...plus this app's tree and rock placements as blockers 19 19

And in region [4096,4096], where the shipped model already agrees to 10.6%:

model placed game
shipped 157 142
plus tree + rock blockers 155 142

Trees occupy 34.3% of region 0 and rocks 3.8%, and both sort before spawners in
autoplace order, so the game finds that ground already taken. Region 0 is the
spawn-centred window where trees are densest, which is why it is the region that
shows it.

The mechanism is not a guess either - it is what the game's own source says. Both
crude-oil and sulfuric-acid-geyser carry order = "c" with the comment:

Other resources are "b"; oil won't get placed if something else is already there.

Why it was not fixed in the placement-roll work

Modelling it needs a tree placement roll, and trees currently render an expected
coverage blend rather than placing individual entities. That roll would itself be
unvalidated - there is no tree entry in the entity-count oracle - so the 19-vs-19
above rests on an unvalidated input. Shipping it would trade a known 47% residual in
one region for an unknown error in every region, on every overlay.

Owner ruling during that work: ship as-is, file this.

What doing it properly looks like

  1. Add trees to the entity-count oracle (test/oracle/entityCounts.ts) and validate a
    tree placement roll on its own, the way every other overlay was.
  2. Then make occupancy a first-class input to makePlacementSet - a shared per-chunk
    occupancy map that overlays write into in autoplace order, rather than each overlay
    colliding only against itself.
  3. Re-measure every overlay. This changes densities globally, so all bands in
    test/entityDensity.spec.ts need re-pinning, not just the enemy rows.

Note that step 2 partially re-introduces the cross-overlay coupling the M3.5 spike
stopped on (docs/noise/placement-roll-NOTES.md). It is strictly weaker - occupancy
in autoplace order, not a shared RNG stream with data-dependent jitter draws - and it
would still not make positions tile-exact. Worth being explicit about that so this
does not get mistaken for "finish the tile-exact port".

Definition of done

  • A tree placement roll validated against its own oracle counts.
  • Occupancy shared across overlays in autoplace order.
  • test/entityDensity.spec.ts re-pinned across all overlays, with the enemy region 0
    row banded rather than carrying its current "unbanded by owner ruling" exemption.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions