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
- 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.
- 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.
- 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.
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], againsttest/fixtures/oracle-entity-counts.seed123456.json(2.1.12, seed 123456):And in region
[4096,4096], where the shipped model already agrees to 10.6%: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-oilandsulfuric-acid-geysercarryorder = "c"with the comment: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
test/oracle/entityCounts.ts) and validate atree placement roll on its own, the way every other overlay was.
makePlacementSet- a shared per-chunkoccupancy map that overlays write into in autoplace order, rather than each overlay
colliding only against itself.
test/entityDensity.spec.tsneed 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 - occupancyin 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
test/entityDensity.spec.tsre-pinned across all overlays, with the enemy region 0row banded rather than carrying its current "unbanded by owner ruling" exemption.