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:
@@ -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`.
|
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
|
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.
|
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;
|
MemoCat = 0;
|
||||||
bAnyNonCave = true;
|
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)
|
else if (StrateManager->GetGeneratorTypeForChunk(CC) == ECaveGeneratorType::SurfaceWorld)
|
||||||
{
|
{
|
||||||
// Retrouve (ou résout) le slot de strate — l'identité vient des bornes chunk-Z du layout.
|
// 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é)
|
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
|
else
|
||||||
{
|
{
|
||||||
FSurfSlot& S = Slots[MemoSlotIdx];
|
FSurfSlot& S = Slots[MemoSlotIdx];
|
||||||
|
|||||||
Reference in New Issue
Block a user