feat(cliffs): capture cliff_orientation, and localise #18 to the crossings (#18) - #75
Merged
Merged
Conversation
…sings
The cliff dumps recorded only {x, y, name}. Both now carry each entity's
`cliff_orientation`, and `placedCells` returns the crossing code it
already computed, so the comparison scores the shipping path rather than
a parallel re-derivation.
Re-capturing reproduced every prior position exactly - Nauvis 282/52 in
the same order, Vulcanus 283/885/409 - so only the new column changed.
Nauvis's fixture moves from a 2.1.11 capture to 2.1.12 in the process,
which also shows Nauvis cliff placement did not move between those
versions.
CLIFF_CODE_TO_ORIENTATION is now confirmed against the game: all 334
Nauvis cliffs match exactly. Its only previous check compared it against
the same jump table it was read from, where a misread and a
mistranscription would have agreed.
Vulcanus does not match, and that is issue #18 seen up close. Over the
1400 cells the port and the game both place, 175 have the wrong
orientation - 29.8% at [0,0], 8.1% at [1500,1500], 11.7% at [-1200,800].
Since the table is right, those are disagreements about the four
CROSSINGS. The dominant failure is exactly two edges differing (125 of
175): one of the cell's two crossings on the wrong side, i.e. a single
corner on the wrong side of a band boundary. Errors spread evenly over
L:87 R:80 T:87 B:89, so it is not a directional off-by-one. A cell can
land in the right place for the wrong reason, and 175 do - which counts
alone could never show.
Three causes tested against the new metric, all three fail. Re-running
PR #57's field substitution leaves it at 67/830, identical to the digit,
while a +3 bias moves it to 122/793 - so the substitution is live, the
metric is sensitive, and the fields are right; #57 scored placement only,
one bit per cell. fixImpossibleCells accounts for 12.5% vs 14.3%. Chunk
borders are 13.3% against interior 11.9%, no concentration.
Also read, and recorded in cliffs-NOTES.md: generateCliffs' full body
(confirms the code packing, the inlined orientation table, and that
grid_offset is applied to the CENTRE only), the fifth `!onChunkBorder`
argument to tryToAddCliff, tryToAddCliff's two sequential paths - the
earlier note described only the collision one - and that crater-cliff is
an entity autoplace, not a lattice cliff.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013eczw9uoWDfN6Wc6kA7UBY
wormeyman
added a commit
that referenced
this pull request
Jul 30, 2026
…er (#76) PR #75 exposed that "substituting the game's own fields moves nothing" had only ever been measured on the corner-fields fixture's regions - [1500,1500], [1100,2600], [-1700,1900] - which were chosen for issue #24 and are all calcite-dominated. Only one of them is a region the cliff port is scored on, and it is the one the port already handles best (8.1% orientation error). [0,0], at 29.8%, had never had its fields checked. This captures both fields at every corner of all three cliff-entity regions (12,675 corners) and scores the substitution on ORIENTATION - four bits per cell against the game's own cliff_orientation, where PR #57 scored placement at one bit. [1500,1500] is deliberately in both fixtures. The overlap is the check on this capture's corner indexing, since an off-by-one there would look exactly like a field error at [0,0]; the two agree bit-for-bit on all 4225 shared corners. Result: the game's own values reproduce ours to the unit in every region, including [0,0] - same cells placed, same matched, same wrong (335/228/68, 1065/830/67, 375/342/40). The +3 bias arm moves both placement and orientation in all three, so the substitution is live everywhere it is claimed to be. So the entire residual is in the rule as ported, with no input left to suspect: crossingsForChunk's sampling geometry, the cliff_smoothing knot model, or crossesCliff. crossingsForChunk (0x10160c9cc, 2244 bytes) is the one never decompiled whole. Also corrects the older fixture's comment, which claimed vulcanus_elevation was sampled only at the smoothing knots. It never was: the corner set is a full nested loop over each region and both samples take all of it, which 3 x 65 x 65 = 12675 confirms. Claude-Session: https://claude.ai/code/session_013eczw9uoWDfN6Wc6kA7UBY 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.
Does both follow-ups from #74.
1. The fixtures now carry
cliff_orientationThe cliff dumps recorded only
{x, y, name}. Both now record each entity'sLuaEntity.cliff_orientation(a 20-value string union whose order matchesCLIFF_ORIENTATION_NAMESexactly, checked).placedCellsreturns the crossingcodeit already computed, so the comparison scores the shipping path rather than a parallel re-derivation.Re-capturing reproduced every prior position exactly - Nauvis 282/52 in the same order, Vulcanus 283/885/409 - so only the new column changed. Nauvis's fixture moves from a 2.1.11 capture to 2.1.12 in the process, which incidentally shows Nauvis cliff placement did not move between those versions.
CLIFF_CODE_TO_ORIENTATIONis confirmed against the game: all 334 Nauvis cliffs match exactly. Its only previous check was against the same jump table it was read from, where a misread and a mistranscription would have agreed.2. Vulcanus does not match - #18, seen up close
[0,0][1500,1500][-1200,800]Since the table is right, these are disagreements about the four crossings. The dominant failure is exactly two edges differing (125 of 175) - one of the cell's two crossings on the wrong side, i.e. a single corner on the wrong side of a band boundary. Errors spread evenly over L:87 R:80 T:87 B:89, so it is not a directional off-by-one.
A cell can land in the right place for the wrong reason, and 175 do. Counts could never show that.
Three causes tested against the new metric; all three fail
[1500,1500]) leaves the mismatch at 67/830, identical to the digit, while a +3 elevation bias moves it to 122/793. The substitution is live, the metric is sensitive, and the fields are right. measure(#24): no ore/cliff exclusion exists; #18's residual is the RULE, not the fields #57 scored placement only - one bit per cell - so it could not have seen this either way.fixImpossibleCells: off moves the total 12.5% → 14.3%, and[0,0]29.8% → 30.8%. Helps slightly, explains almost nothing.fixImpossibleCellscannot clear an edge.3. Binary reading, recorded in
cliffs-NOTES.mdgenerateCliffs' full body (0x1016229b4, 1080 bytes, decompiled whole). Confirms the code packing including both edge-array strides; confirmstoMaybeCliffOrientationis inlined with exactly the<0x51jump table plus explicit0xC0/0xC1/0xCC/0xF0compares storing(orientation << 32) | 2→ 12/8/4/0, agreeing with our table entry for entry; and confirms the centre formulagrid_offset + chunk*32 + c*grid + (grid >> 1)- sogrid_offsetis applied to the centre and nowhere else, which is fix(cliffs): sample the fields at the game's lattice - Nauvis is now EXACT (#18) #70's finding from the other side.tryToAddClifftakes a fifth argument and it is!onChunkBorder- previously unmodelled. It does not gate the collision test, and measurement says it does not gate placement either.tryToAddCliffhas two paths and the earlier note described only one. It branches on a mode byte atthis + 0x10;mode == 2does the collision test, otherwise none. They are sequential, not alternatives - on a miss the collision path falls through into the same tail, which appends a 16-byte record to a vector, storing that fifth argument at+0xc. OurtileCollidespost-filter models themode == 2path, which is the one map generation takes.crater-cliffis an entity autoplace, not a lattice cliff - confirmed rather than assumed, fromdecoratives-vulcanus.lua:2776(scaled_cliff_crater,probability_expression = "crater_cliff") andplanet-map-gen.lua:122. Excluding it is correct.The gap this exposes, and the next capture
No field capture covers
[0,0]. The corner-fields fixture's three regions are[1500,1500],[1100,2600]and[-1700,1900]- all calcite regions chosen for issue #24. So "the fields are exact" has been measured where the port is already good (8.1%) and never where it is worst (29.8%). Capturing corner fields over[0,0]and[-1200,800]is the next step, and it is now a sharp experiment rather than a fishing trip: the orientation metric will answer it at 4 bits per cell.pnpm run verifygreen: 303 files checked, 1272 tests.🤖 Generated with Claude Code
https://claude.ai/code/session_013eczw9uoWDfN6Wc6kA7UBY