fix(classify): out-of-layout chunks are constant AIR, not TunnelNetwork -- unblocks T1.d

Measured in game: Cave Bail Not Op Stack No Layout = 1.58 of 1.90 classified
tiles (83%), with SoleSlot and BoundaryTile never firing. The assets were
correctly ticked all along.

Cause: GetGeneratorTypeForChunk returns TunnelNetwork for any chunk outside the
strate stack, commenting "the fallback density path will produce solid rock
anyway". That comment is FALSE. GetGenerationParams for SlotIdx < 0 returns
BaseDensity = -1, RoomDensity = 0, WormStrength = 0 -- a CONSTANT AIR field.
IsGapChunk's "open air, NOT a gap" was the correct description.

So every tile touching the open air above the world looked like a cave
archetype, entered the cave branch, found no layout slot, and bailed. T1.d was
never failing -- it was unreachable, behind a routing mistake in the archetype
lookup that had nothing to do with the operator stack, the box verdicts, or the
flags.

Fix: ClassifyTile gains a fourth Z category for no-slot chunks. It sets
bAnyNonCave (never enters the cave branch) and bCanSolid = false (AllAir
survives), and bails to Mixed if a disturbance could add rock there -- chasms
only carve, so they cannot threaten an air verdict.

A tile entirely above the stack now resolves AllAir and is skipped: the
majority of a surface flight, a class T1.d has never been able to prove.

Classifier only -- no density value changes. Equivalences must stay
bit-identical and violations must stay 0.

Not built -- Jahni builds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 01:20:06 +02:00
parent 05986bd875
commit a2c5e02713
2 changed files with 79 additions and 0 deletions
+40
View File
@@ -4362,3 +4362,43 @@ sitting behind a routing mistake in the classifier's archetype lookup.
out-of-layout chunks their own category in `ClassifyTile` rather than borrowing `TunnelNetwork`.
If open air above the stack is provably uniform, those tiles become an `AllAir`/`AllSolid` verdict
and T1.d finally fires on the majority of a surface flight.
## 2026-08-16 (s) — T1.d BLOCKER FIXED: out-of-layout is constant AIR, not a cave archetype
The contradictory comments are settled by reading `GetGenerationParams`:
```cpp
// If outside all strates, return negative density → guaranteed air.
if (SlotIdx < 0) {
Empty.BaseDensity = -1.0f; Empty.WormStrength = 0.0f; Empty.RoomDensity = 0.0f;
```
**`GetGeneratorTypeForChunk`'s comment ("the fallback density path will produce solid rock
anyway") is FALSE.** Out-of-layout is a **constant air field** — no rooms, no worms, nothing to
carve or fill it. `IsGapChunk`'s "open air, NOT a gap" was the correct one.
### The fix
`ClassifyTile` gains a fourth Z category (`MemoCat = 3`) for chunks with no layout slot:
- sets `bAnyNonCave = true`**never enters the cave branch**, which is where all 83 % were dying;
- sets `bCanSolid = false` ⇒ the AllSolid hypothesis dies, **AllAir survives**;
- bails to `Mixed` if a disturbance could *add* rock there (`BridgeDensity`/`RidgeDensity` > 0) —
chasms only carve, so they cannot threaten an air verdict.
A tile entirely above the stack now resolves: no cave block, no column scan (`NumSlots == 0`),
`bCanSolid = false`, `bCanAir = true`**`AllAir`, skipped.** That is the majority of a surface
flight, and it is a class of tile T1.d has never once been able to prove.
### What to read after the build
- **`Cave Bail Not Op Stack No Layout` should collapse to ~0** — those tiles no longer reach the
cave branch at all.
- **`Tiles Skipped All Air` should rise sharply**, and `Tiles Meshed` should fall below
`Tiles Classified` for the first time in the game.
- ⚠️ **`violations` must stay 0** in every test, and the eight equivalences must stay bit-identical:
this changes only the *classifier*, never a density value.
Compile-risk spots: `FindSlotIndexForChunkZ` is public (`VoxelStrateManager.h:351`, verified);
`FStrateDisturbanceParams` is already used later in the same function; the new `else if` sits before
the surface `else`, so `Slots[MemoSlotIdx]` is never dereferenced for the new category.
@@ -2777,6 +2777,37 @@ EVoxelTileClass UVoxelGenerator::ClassifyTile(const FIntVector& OriginVoxels, in
MemoCat = 0;
bAnyNonCave = true;
}
//=================================================================
// ⛔ HORS LAYOUT = AIR CONSTANT. C'ÉTAIT LE BLOCAGE DE T1.d.
//=================================================================
// `GetGeneratorTypeForChunk` rend `TunnelNetwork` pour tout chunk hors de la pile de
// strates (« le chemin de repli produit de la roche de toute façon » — CE COMMENTAIRE
// EST FAUX) et `IsGapChunk` rend false au-dessus du sommet (« open air, NOT a gap »).
// Résultat : chaque tuile touchant l'air libre au-dessus du monde entrait dans la
// BRANCHE DE CAVE, n'y trouvait aucun slot, et abandonnait — mesuré en jeu à 83 % des
// tuiles classées (`Cave Bail Not Op Stack No Layout` = 1.58 / 1.90).
//
// La vérité est dans `GetGenerationParams` : hors layout il rend `BaseDensity = -1`,
// `RoomDensity = 0`, `WormStrength = 0` — un champ CONSTANT, donc de l'air, sans salle
// ni ver pour le percer. Une telle tuile est prouvable sans échantillonner.
//
// Out-of-layout is a CONSTANT AIR field, not a cave archetype. Every tile touching the
// open air above the world was being routed into the cave branch and bailing there.
else if (StrateManager->FindSlotIndexForChunkZ(ChunkZ) < 0)
{
MemoCat = 3;
bAnyNonCave = true; // n'entre JAMAIS dans la branche de cave
// Les disturbances sont appliquées APRÈS la densité d'archétype et peuvent AJOUTER
// de la roche (ponts, arêtes). Même prudence que les branches gap et cave : si
// l'une peut agir ici, on ne prouve rien. Les chasms ne font que creuser ⇒ ils ne
// menacent pas un verdict d'air.
const FStrateDisturbanceParams DOut = StrateManager->GetDisturbanceParamsForChunk(CC);
if (DOut.BridgeDensity > 0.0f || DOut.RidgeDensity > 0.0f)
{
return EVoxelTileClass::Mixed;
}
}
else if (StrateManager->GetGeneratorTypeForChunk(CC) == ECaveGeneratorType::SurfaceWorld)
{
// Retrouve (ou résout) le slot de strate — l'identité vient des bornes chunk-Z du layout.
@@ -2888,6 +2919,14 @@ EVoxelTileClass UVoxelGenerator::ClassifyTile(const FIntVector& OriginVoxels, in
{
bCanAir = false; // bedrock du gap = solide (le carve des passages est déjà gardé)
}
else if (MemoCat == 3)
{
// Hors layout = air constant (BaseDensity = -1, aucune salle, aucun ver). L'hypothèse
// « tout solide » meurt ; « tout air » survit. Les passages et la spine ne font que
// creuser — ils sont déjà gardés plus haut et ne peuvent pas rendre ce z solide.
// Out of layout = constant air: AllSolid dies, AllAir survives.
bCanSolid = false;
}
else
{
FSurfSlot& S = Slots[MemoSlotIdx];