Skip to content

docs(cliffs): read crossingsForChunk whole - it matches the port (#18) - #77

Merged
wormeyman merged 1 commit into
mainfrom
docs/crossings-for-chunk-read
Jul 30, 2026
Merged

docs(cliffs): read crossingsForChunk whole - it matches the port (#18)#77
wormeyman merged 1 commit into
mainfrom
docs/crossings-for-chunk-read

Conversation

@wormeyman

Copy link
Copy Markdown
Owner

Documentation only. Records the last unread function in the cliff path, plus three more falsified causes and two closed leads.

crossingsForChunk read whole - and it matches

0x10160c9cc, 2244 bytes, decompiled entire. Every structural element agrees with what is ported, so these are now confirmed rather than assumed:

  • The bare sample lattice (chunkX*32 + i*grid, chunkY*32 + j*grid) over a 9x9 corner block - no grid_offset anywhere. fix(cliffs): sample the fields at the game's lattice - Nauvis is now EXACT (#18) #70's finding, seen from the producing side.
  • The smoothing knots: lo = i & ~3, hi = lo + 4 clamped to w - 1 (the uVar25 = uVar25 - 1 before the loop is where the 7 comes from), t = (i & 3) / (hi - lo), blend s * bilerp + (1 - s) * raw with s at settings + 0xd0.
  • Which register is smoothed: 0x1e0 (elevation) into a fresh array; 0x1e4 (cliffiness) read raw at the same indices.
  • crossesCliff inlined, matching including the cliffinessAvg > 0.5 gate.
  • Both edge arrays: horizontal (h+1) x w at x + w*y, vertical (w+1) x h at x + y*(w+1), with generateCliffs reading L/R from vertical and T/B from horizontal.
  • Tail call fixImpossibleCells(this, false).

Three more causes falsified, with denominators

  • float vs double is not it. The engine works at 32 bits and the port at 64, which fits the signature (one corner flipping near a band boundary). Replaying the whole rule with Math.fround at every step changes nothing: 0 of 12,480 raw edges per region, identical placement and orientation.
  • The (int) vs Math.floor difference is real but inert. The engine truncates toward zero where the port floors. They differ only when max(a,b) < elevation_0, and there both give "no crossing" - the band lands at or below elevation_0 and both corners are below it, so neither sign case can fire.
  • The smoothing model is right in kind, not merely unfalsified. cliff_smoothing = 0 makes the orientation error much worse - 29.8% → 71.3%, 8.1% → 64.1%, 11.7% → 54.5%. That is the control the disassembly reading needed.

Also: the float table where a smoothing-weight table would live (0x102cf9cf0) is just 0.0..31.0, the SIMD lane-index constant.

Two leads closed from documentation

  • The expression choice - which no substitution can test, since the corner-field fixtures capture vulcanus_elevation and cliffiness_basic by name. Closed from the data: planet-map-gen.lua:13-14 routes cliffiness = "cliffiness_basic" and cliff_elevation = "cliff_elevation_from_elevation", and that is literally expression = "elevation". Worth knowing the alternative was live: the default cliff_elevation is cliff_elevation_nauvis, a completely different field - the same shape of trap as the cliff_smoothing default that cost Vulcanus cliffs: 12.5% of matched cells get the wrong cliff_orientation (residual after the placement-count fixes) #18 two months.
  • FFF #219 (the cliff design writeup) checked and recorded so nobody spends the time again. It confirms the algorithm's shape, but note two things: it is from 2017 and describes the pre-2.0 algorithm, and its description of the repair pass is a simplification that would be a bug if followed. It says edges are removed "until no cell has more than 2 cliff-crossing edges" - but of the 20 placing codes 8 have one crossing and 12 have two, against C(4,2)*2*2 = 24 possible two-crossing codes, so 12 two-crossing codes are illegal. A count-based predicate would leave those in, and generateCliffs asserts and aborts on an illegal code.

FFF #386 corroborates two things we had only from the binary. Crater cliffs exist because "the collision boxes are always rectangles so hitting invisible corners is annoying" - developers stating outright that cliff collision boxes are plain rectangles, which is independent confirmation for CLIFF_ORIENTATION_COLLISION_BOX and the rotbb AABB derivation. And "a ring of special cliffs where sections of the ring can randomly be removed" is not a lattice structure, which is why their positions are fractional. They also cannot be #18's residual, stated with the count rather than by argument: [0,0] has zero crater-cliffs and [1500,1500] has zero; all 8 are in [-1200,800].

Where #18 stands

Fields exact (including at [0,0]), rule structurally confirmed, precision irrelevant, smoothing confirmed, expression choice confirmed - and 12.5% of shared cells still carry the wrong crossings. What survives is fixImpossibleCells in detail: it accounts for only 1.8 of the 12.5 points as ported, but "our sweep differs subtly from the game's" is not excluded by that - a different sweep could be both closer and worth more. FFF #219's note that the pass prioritises chunk boundaries agrees with the port's "a boundary edge is not clearable".

pnpm run verify green: 304 files checked, 1279 tests.

🤖 Generated with Claude Code

https://claude.ai/code/session_013eczw9uoWDfN6Wc6kA7UBY

Records the last unread function in the cliff path (0x10160c9cc, 2244
bytes). Every structural element agrees with what is ported, so these
are now confirmed rather than assumed: the bare sample lattice
(chunkX*32 + i*grid, no grid_offset), the smoothing knots (lo = i & ~3,
hi = min(lo + 4, w - 1), t = (i & 3) / (hi - lo), blend
s*bilerp + (1-s)*raw with s at settings+0xd0), which register is
smoothed (0x1e0) versus read raw (0x1e4), crossesCliff inlined with the
cliffiness > 0.5 gate, both edge array shapes and strides, and the tail
call to fixImpossibleCells(this, false).

Three more causes falsified with denominators. float vs double is not
it: replaying the whole rule with Math.fround at every step changes
nothing, 0 of 12,480 raw edges per region and identical placement and
orientation. The engine's (int) truncation where the port floors is a
real textual difference but inert - the two differ only when
max(a,b) < elevation_0, where neither sign case can fire. And the
smoothing model is right IN KIND rather than merely unfalsified:
cliff_smoothing = 0 makes the orientation error much worse
(29.8 -> 71.3%, 8.1 -> 64.1%, 11.7 -> 54.5%).

Also notes that the float table where a smoothing-weight table would
live is just 0.0..31.0, the SIMD lane-index constant.

What survives: fixImpossibleCells in detail, and the choice of
expression - which no substitution can test, because the corner-field
fixtures capture vulcanus_elevation and cliffiness_basic BY NAME. If a
different expression compiles into settings+0x1e0, the substitution
agrees with the port for the same reason the port is wrong. Settle that
by reading 0x1e0, not by substituting more values.

Documentation only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013eczw9uoWDfN6Wc6kA7UBY
@wormeyman
wormeyman merged commit e31a516 into main Jul 30, 2026
2 checks passed
@wormeyman
wormeyman deleted the docs/crossings-for-chunk-read branch July 30, 2026 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant