|
24 | 24 | > come from. Each of the last three corrects the one before it - read all three, |
25 | 25 | > in order, or you will act on a superseded number. |
26 | 26 | > |
| 27 | +> ## UPDATE 2, 2026-08-02: the BLOB is ORE - #84 item 2 is #24 |
| 28 | +> |
| 29 | +> The "blob" - the contiguous patch in `[0,0]` where the game places no cliff |
| 30 | +> whatever `cliff_elevation` is routed onto it - **is a tungsten-ore field**. The |
| 31 | +> game does not put cliffs on ore (3 of its 1,569 cliffs across the three oracle |
| 32 | +> regions do) and the port does. Read the LAST section, |
| 33 | +> **`## The blob is ore`**; it supersedes the "field-independent suppression of |
| 34 | +> unknown cause" framing below. Two corrections ride with it: |
| 35 | +> |
| 36 | +> - **`37 / 1531 = 2.4%` is the NO-lava-rejection arm**, not what ships. With the |
| 37 | +> rejection `renderVulcanusCliffs` actually applies it is **33 wrong of 1,525 |
| 38 | +> matched**, plus 45 over-placed and 6 missed cells. Both arms are now pinned in |
| 39 | +> `test/cliffOreExclusion.spec.ts` so they cannot be confused again. |
| 40 | +> - **The cliffiness gate is exact** - 0 flips over all 24,960 captured edges of |
| 41 | +> the three regions, measured as the boolean `crossesCliff` reads rather than as |
| 42 | +> a value. That closes the clamp-vacuity worry properly. |
| 43 | +> |
27 | 44 | > ## UPDATE, 2026-08-02: the FIELD is exonerated; the residual is two defects |
28 | 45 | > |
29 | 46 | > The last section, **`## The residual is TWO defects, and the field is not |
@@ -1320,3 +1337,108 @@ therefore structurally could not find that class of error). |
1320 | 1337 | **This is the sharpest open lead**, and a much better one than "we over-place at |
1321 | 1338 | low elevation": it is a bounded, contiguous, field-independent suppression, so |
1322 | 1339 | whatever causes it is a rule the port does not implement at all. |
| 1340 | + |
| 1341 | +## The blob is ore (2026-08-02, #84 item 2 -> #24) |
| 1342 | + |
| 1343 | +The section above called the blob "the sharpest open lead", on the reasoning that |
| 1344 | +a bounded, contiguous, field-independent hole cannot be a field error and must be |
| 1345 | +a rule the port does not implement at all. That reasoning was right and the rule |
| 1346 | +is **ore exclusion**. The patch is a tungsten-ore field. |
| 1347 | + |
| 1348 | +Measured in `test/cliffOreExclusion.spec.ts`, over the three oracle regions: |
| 1349 | + |
| 1350 | +| | game | port | |
| 1351 | +| --- | --- | --- | |
| 1352 | +| cliffs whose 4x4 cell contains an ore tile | **3** of 1,569 | 29 | |
| 1353 | +| of the port's 45 surplus cells | - | **26** on ore, all at `[1500,1500]` | |
| 1354 | +| the collapsed arms' 10-cell blob | 0 | **10 of 10 on ore** | |
| 1355 | + |
| 1356 | +The blob's ten cells are `x 178/182/186`, `y 138.5-150.5`, and they are the |
| 1357 | +**same ten in all four collapsed arms** - gate open and gate real, single contour |
| 1358 | +and real bands. A set that is invariant to both the band structure and the gate |
| 1359 | +cannot be a field or a cliffiness effect. |
| 1360 | + |
| 1361 | +The handoff quoted a looser envelope (`cx 43-48, cy 34-40`, world |
| 1362 | +`x 172-196, y 136-164`). That is the union over the 19-level `cliff_elevation_0` |
| 1363 | +sweep; the arm-invariant core is the ten. |
| 1364 | + |
| 1365 | +**Note what this does to the scope.** At real settings the port places nothing in |
| 1366 | +the blob at all - `[0,0]` scores 283/283 with 2 surplus and 2 missed, neither in |
| 1367 | +the blob. The blob is only reachable when a sweep forces a contour through the |
| 1368 | +ore field. The ore rule costs real accuracy only at `[1500,1500]`, where it is |
| 1369 | +26 of that region's 42 surplus cells. |
| 1370 | + |
| 1371 | +### What it is NOT, each measured rather than read |
| 1372 | + |
| 1373 | +- **Not lava.** 2,597 `surface.get_tile` samples from the game over |
| 1374 | + `x 160..208, y 124..176`, 0 lookup misses: the blob's interior is |
| 1375 | + `volcanic-cracks-warm` / `-hot` / `volcanic-smooth-stone`, with no lava |
| 1376 | + anywhere in it. The standing "not lava" claim had been made with OUR resolver, |
| 1377 | + which is exactly the component it needed to exonerate - and which is known to |
| 1378 | + miss lava the game has (4 tiles in 483 in this same neighbourhood). |
| 1379 | +- **Not the cliffiness gate.** 0 flips over 24,960 edges; see below. |
| 1380 | +- **Not any other tile.** Read off a running game rather than deduced: |
| 1381 | + `cliff-vulcanus`'s collision mask is |
| 1382 | + `cliff, is_lower_object, is_object, item, meltable, object, player, water_tile`, |
| 1383 | + and of the 18 Vulcanus tile prototypes only `lava` and `lava-hot` share a layer |
| 1384 | + with it (`item`, `player`, `water_tile`). Every other tile, including |
| 1385 | + `volcanic-jagged-ground` - the tile ore patches paint, which the Lua labels |
| 1386 | + "CLIFF TILE" - is `ground_tile` only. **`VULCANUS_CLIFF_BLOCKING_TILES` is now |
| 1387 | + measured, not inferred from `tile_collision_masks.lava()`.** |
| 1388 | +- **Not entity collision**, and this one is worth its own note because a real |
| 1389 | + unported rule was found while ruling it out. `applyCliffs` (`0x101623c98`) |
| 1390 | + re-tests every accepted cliff through **`Surface::wouldCollide`** |
| 1391 | + (`0x10160c088`), which calls `constCollideWithTile` **and** |
| 1392 | + `collideWithEntity`. The port implements neither - only the task-level |
| 1393 | + `EntityMapGenerationTask::wouldCollide` (`0x101625468`), which is **tile-only** |
| 1394 | + (it indexes `Tile::collisionMasks` over a 96x96 working-area grid and treats |
| 1395 | + anything outside that grid as a collision). So there IS a second, entity-aware |
| 1396 | + rejection that is not ported. It cannot be what excludes ore, though: |
| 1397 | + `tungsten-ore`, `calcite`, `coal` and `sulfuric-acid-geyser` all carry the bare |
| 1398 | + `resource` layer, which the cliff mask does not hold. |
| 1399 | + |
| 1400 | + What DOES collide with a cliff and is not ported: `big-volcanic-rock`, |
| 1401 | + `huge-volcanic-rock` (both `is_lower_object, is_object, item, meltable, object, |
| 1402 | + player, water_tile`) and **`crater-cliff`** - a Vulcanus cliff-type entity |
| 1403 | + placed by autoplace (`probability_expression = "crater_cliff"`, |
| 1404 | + `crater_radius = 7`, `crater_edge_thickness = 5`). Craters are already in the |
| 1405 | + fixtures and filtered out by name: 8 in `[-1200,800]`, 3 near `(0, 164)` in the |
| 1406 | + collapsed arms. Only two rocks touch the blob, so rocks explain at most 1 of |
| 1407 | + its 10 cells - but this is a genuine open item for the wider residual. |
| 1408 | + |
| 1409 | +So the exclusion is real and its **mechanism is still open**. It is not a |
| 1410 | +collision, so it is either an ordering effect inside `EntityMapGenerationTask` |
| 1411 | +(`generateCliffs` runs before `generateEntities`, so the direction may be that |
| 1412 | +ore avoids cliffs rather than cliffs avoiding ore) or something in |
| 1413 | +`resource_autoplace` that the cliff pass reads. The next step is to settle the |
| 1414 | +direction, not to add a rejection. |
| 1415 | + |
| 1416 | +### The cliffiness gate, measured as the BINARY its consumer reads |
| 1417 | + |
| 1418 | +`cliffiness_basic` is `clamp(qmn, 0, 1) + 0.5` and two thirds of its captured |
| 1419 | +corners sit ON a clamp, which is why the value comparison was two-thirds |
| 1420 | +vacuous. But what the consumer reads is `crossesCliff`'s gate, and that is a |
| 1421 | +**threshold**: |
| 1422 | + |
| 1423 | +``` |
| 1424 | +crossesCliff(a, b, cliffiness, e0, interval) // 0x10160c914 |
| 1425 | + if (a < 0 || b < 0) return 0 |
| 1426 | + level = e0 + interval * floor((max(a,b) - e0) / interval) |
| 1427 | + if (level < e0) return 0 |
| 1428 | + if (cliffiness > 0.5) { if (a-level < 0 && b-level > 0) return +1 |
| 1429 | + if (a-level > 0 && b-level < 0) return -1 } |
| 1430 | + return 0 |
| 1431 | +``` |
| 1432 | + |
| 1433 | +and `crossingsForChunk` feeds it the **average of the two corners** |
| 1434 | +(`0x10160d1cc`, and again at `0x10160d06c` for the vertical edges). A clamped |
| 1435 | +corner is therefore not vacuous for the gate at all - it is precisely where an |
| 1436 | +arbitrarily small error flips the answer. Scored as a boolean over every captured |
| 1437 | +edge of all three regions the port agrees with the game on **all 24,960**, with |
| 1438 | +13,661 open and 11,299 shut, so a constant predicate could not pass. |
| 1439 | + |
| 1440 | +Also re-derived while reading `crossingsForChunk` end to end, and matching the |
| 1441 | +port: the corner lattice is the bare `chunkOrigin + (i, j) * grid_size`, the |
| 1442 | +elevation register is smoothed with knots at in-chunk indices `{0, 4, 7}` |
| 1443 | +(`hi = min(lo + 4, 7)`) and **cliffiness is read unsmoothed**, and |
| 1444 | +`getModifiedElevationInterval` is `cliff_elevation_interval / frequency`. |
0 commit comments