passages: four-strate passage floors supported (L67, evaluator v212); 1-2 wall beat still red

- Shared composer: a passage's restored support is applied after a physically overlapping
  later bore, instead of being carved away by it (0-1: 59 -> 0 unsupported).
- Capsule clearance for support restoration is tested against nearby route segments only
  (1-2: 3 -> 0, 2-3: 2 -> 0 unsupported).
- L66 index switches replaced by a physical criterion (both mouths inside their strata bands,
  neither a SurfaceWorld landing).
- Audit: floor zero-crossing refined by bisection (the 0.2498 m "step" was the gauge);
  wall ripple keeps the authored radius, cubic detrend per 32 m window. Controls detected:
  L35 ribs, beads, synthetic 0.25 m step, L36 washboard, L23 terrace.
- Result: 0-1 and 2-3 pass all audits; 1-2 fails wall ripple 7.27 cm (limit 3.5) from an
  authored-radius beat at 7.06 m. Fast tier was green before the final gauge change and was
  not re-run after it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-27 02:27:40 +01:00
1 parent 5e2fc53d39
commit 087333bf8a
17 files changed
+1439 -43

No files matched your search

+325 -4
View File
@@ -5,6 +5,7 @@
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iomanip>
#include <limits>
#include <sstream>
@@ -2459,18 +2460,29 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
}
std::uint32_t phase=static_cast<std::uint32_t>(PassageSeed(seed,
upper.strateIndex,lower.strateIndex,connection,0x50484153u)%16);
if(hasUpperRoute)
// Authored tangents and island anchors already determine the phase.
// A generated mouth in a native cave uses its radial approach; an
// island landing uses the tangent fit below to ease off the crater rim.
const bool generatedIslandWormEntry=!hasUpperRoute&&!hasUpperTangent
&&!anchoredSpiral&&upper.generatorKind=="FloatingIslands";
if(hasUpperRoute||(anchoredIslandPassage&&!anchoredSpiral)
||(!hasUpperRoute&&!anchoredIslandPassage&&!anchoredSpiral
&&upper.generatorKind!="FloatingIslands"))
{
std::int64_t bestAlignment=std::numeric_limits<std::int64_t>::min();
const std::uint32_t hashedPhase=phase;
const std::int64_t phaseX=style==0&&anchoredIslandPassage
&&!anchoredSpiral&&passageAnchor
?upperMouth[0]-passageAnchor->values[0]
:(hasUpperTangent?upperTangent[0]:upperMouth[0]);
:(hasUpperTangent?upperTangent[0]:upperMouth[0]
-(anchoredIslandPassage&&passageAnchor
?passageAnchor->values[0]:0));
const std::int64_t phaseY=style==0&&anchoredIslandPassage
&&!anchoredSpiral&&passageAnchor
?upperMouth[1]-passageAnchor->values[1]
:(hasUpperTangent?upperTangent[1]:upperMouth[1]);
:(hasUpperTangent?upperTangent[1]:upperMouth[1]
-(anchoredIslandPassage&&passageAnchor
?passageAnchor->values[1]:0));
for(std::uint32_t offset=0;offset<16;++offset)
{
const std::uint32_t candidate=static_cast<std::uint32_t>(
@@ -2528,7 +2540,7 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
clockwise=clockwiseTangentX*upperTangent[0]
+clockwiseTangentY*upperTangent[1]>=0.0;
}
const int direction=clockwise?1:-1;
int direction=clockwise?1:-1;
constexpr std::int64_t TargetGradeQ16=12500; // leave margin below the 15-degree helix contract.
const std::int64_t wormRadialWobble=std::min<std::int64_t>(
std::max<std::int64_t>(0,wander/3),2*qPerMetre);
@@ -2597,6 +2609,267 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
static_cast<std::int64_t>(std::llround(centerX+radius*directions[directionIndex][0]/65536.0)),
static_cast<std::int64_t>(std::llround(centerY+radius*directions[directionIndex][1]/65536.0))};
};
if(generatedIslandWormEntry)
{
struct FPhaseScore
{
std::uint32_t phase=0;
int direction=1;
std::uint64_t routeLength=0;
double clearance=0.0;
double tangent=0.0;
};
std::array<FPhaseScore,32> phaseScores{};
std::size_t phaseScoreCount=0;
const std::uint32_t hashedPhase=phase;
const int hashedDirection=direction;
const std::int64_t entryVerticalDistance=std::abs(
lowerMouth[2]-upperMouth[2]);
const std::int64_t entryRouteGradeQ16=profiledSurfaceJoin?8000
:style==0&&anchoredIslandPassage&&!anchoredSpiral?15500
:TargetGradeQ16;
const auto candidateRouteLength=[&](std::uint32_t candidate,int candidateDirection)
{
std::vector<std::array<std::int64_t,2>> candidateXY;
const auto append=[&](std::array<std::int64_t,2> point)
{if(candidateXY.empty()||candidateXY.back()!=point)
candidateXY.push_back(point);};
append({upperMouth[0],upperMouth[1]});
append(ringPoint(candidate));
const auto measure=[&]()
{
std::uint64_t length=0;
for(std::size_t i=1;i<candidateXY.size();++i)
length+=distanceQ(candidateXY[i][0]-candidateXY[i-1][0],
candidateXY[i][1]-candidateXY[i-1][1]);
length+=distanceQ(lowerMouth[0]-candidateXY.back()[0],
lowerMouth[1]-candidateXY.back()[1]);
return length;
};
std::uint32_t loops=0;
std::uint64_t length=0;
while(loops<256)
{
length=measure();
if(length*static_cast<std::uint64_t>(entryRouteGradeQ16)
>=static_cast<std::uint64_t>(entryVerticalDistance)*65536ull)break;
for(int step=1;step<=16;++step)
{
int directionIndex=static_cast<int>(candidate)
+candidateDirection*step;
directionIndex=(directionIndex%16+16)%16;
append(ringPoint(static_cast<std::uint32_t>(directionIndex),
loops+(step==16?1u:0u)));
}
++loops;
}
if(loops>=256)return std::uint64_t{0};
const std::int64_t mouthPlanSeparation=distanceQ(
lowerMouth[0]-upperMouth[0],lowerMouth[1]-upperMouth[1]);
if(!hasLowerTangent&&!anchoredSpiral&&pathStyle==1u&&loops<=1
&&mouthPlanSeparation<=2*mouthRadius
&&entryVerticalDistance<=2*mouthRadius+qPerMetre/2)
{
double bestExitScore=-std::numeric_limits<double>::infinity();
std::uint32_t lowerPhase=candidate;
const auto firstRing=ringPoint(candidate);
for(std::uint32_t exitCandidate=0;exitCandidate<16;++exitCandidate)
{
if(exitCandidate==candidate)continue;
const std::uint32_t previous=static_cast<std::uint32_t>(
(static_cast<int>(exitCandidate)-candidateDirection+16)%16);
const auto terminal=ringPoint(exitCandidate,loops);
const auto beforeTerminal=ringPoint(previous,loops);
const double incomingX=static_cast<double>(terminal[0]-beforeTerminal[0]);
const double incomingY=static_cast<double>(terminal[1]-beforeTerminal[1]);
const double outgoingX=static_cast<double>(lowerMouth[0]-terminal[0]);
const double outgoingY=static_cast<double>(lowerMouth[1]-terminal[1]);
const double incomingLength=std::hypot(incomingX,incomingY);
const double outgoingLength=std::hypot(outgoingX,outgoingY);
if(incomingLength<=1.0e-6||outgoingLength<=1.0e-6)continue;
const double alignment=(incomingX*outgoingX+incomingY*outgoingY)
/(incomingLength*outgoingLength);
const double separation=std::hypot(
static_cast<double>(terminal[0]-firstRing[0]),
static_cast<double>(terminal[1]-firstRing[1]));
const double score=alignment+0.20*std::clamp(
separation/(2.0*qPerMetre),0.0,2.0);
if(score>bestExitScore)
{bestExitScore=score;lowerPhase=exitCandidate;}
}
for(int step=1;step<=16;++step)
{
int directionIndex=static_cast<int>(candidate)
+candidateDirection*step;
directionIndex=(directionIndex%16+16)%16;
append(ringPoint(static_cast<std::uint32_t>(directionIndex),loops));
if(static_cast<std::uint32_t>(directionIndex)==lowerPhase)break;
}
}
return measure();
};
for(std::uint32_t offset=0;offset<16;++offset)
{
const std::uint32_t candidate=static_cast<std::uint32_t>(
(hashedPhase+16-offset)%16);
const auto point=ringPoint(candidate);
const double entryX=static_cast<double>(point[0]-upperMouth[0]);
const double entryY=static_cast<double>(point[1]-upperMouth[1]);
const double entryLength=std::hypot(entryX,entryY);
if(entryLength<=1.0e-6)continue;
const auto entryDistance=distanceQ(point[0]-upperMouth[0],
point[1]-upperMouth[1]);
for(const int candidateDirection:{hashedDirection,-hashedDirection})
{
const std::uint64_t routeLength=candidateRouteLength(candidate,
candidateDirection);
if(routeLength==0)continue;
const std::uint32_t next=static_cast<std::uint32_t>(
(static_cast<int>(candidate)+candidateDirection+16)%16);
const auto nextPoint=ringPoint(next);
const double tangentX=static_cast<double>(nextPoint[0]-point[0]);
const double tangentY=static_cast<double>(nextPoint[1]-point[1]);
const double tangentLength=std::hypot(tangentX,tangentY);
if(tangentLength<=1.0e-6)continue;
const double tangentAlignment=(entryX*tangentX+entryY*tangentY)
/(entryLength*tangentLength);
const std::int64_t firstAxisZ=upperMouth[2]+mouthRadius
-playerCenterAboveFloorQ;
const std::int64_t secondAxisZ=firstAxisZ+PassageRoundRatio(
lowerMouth[2]-upperMouth[2],static_cast<std::int64_t>(entryDistance),
static_cast<std::int64_t>(routeLength));
double candidateTreadClearance=std::numeric_limits<double>::infinity();
const int entrySamples=std::max(1,static_cast<int>(std::ceil(
entryLength/(0.25*qPerMetre))));
for(int sample=0;sample<=entrySamples;++sample)
{
const double t=static_cast<double>(sample)/entrySamples;
const double qx=upperMouth[0]+t*entryX;
const double qy=upperMouth[1]+t*entryY;
const double qz=firstAxisZ+t*(secondAxisZ-firstAxisZ)
-mouthRadius+0.20*qPerMetre;
for(const auto& previousPassage:layout.passages)
{
if(previousPassage.stableId==passage.stableId
||previousPassage.controlPoints.size()<2)continue;
for(std::size_t previousSegment=0;
previousSegment+1<previousPassage.controlPoints.size();
++previousSegment)
{
const auto& previousA=previousPassage.controlPoints[previousSegment];
const auto& previousB=previousPassage.controlPoints[previousSegment+1];
const double ax=static_cast<double>(previousA.positionSubvoxels[0]);
const double ay=static_cast<double>(previousA.positionSubvoxels[1]);
const double az=static_cast<double>(previousA.positionSubvoxels[2]);
const double dx=static_cast<double>(previousB.positionSubvoxels[0])-ax;
const double dy=static_cast<double>(previousB.positionSubvoxels[1])-ay;
const double dz=static_cast<double>(previousB.positionSubvoxels[2])-az;
const double horizontalLengthSquared=dx*dx+dy*dy;
const double projection=horizontalLengthSquared>0.0
?std::clamp(((qx-ax)*dx+(qy-ay)*dy)
/horizontalLengthSquared,0.0,1.0):0.0;
const double centerX=ax+projection*dx;
const double centerY=ay+projection*dy;
const double centerZ=az+projection*dz
-(previousA.radiusSubvoxels+projection*(
static_cast<double>(previousB.radiusSubvoxels)
-previousA.radiusSubvoxels))
+1.13*qPerMetre;
const double vertical=std::max(0.0,
std::abs(qz-centerZ)-0.54*qPerMetre);
const double clearance=std::sqrt(
(qx-centerX)*(qx-centerX)+(qy-centerY)*(qy-centerY)
+vertical*vertical)-0.34*qPerMetre;
candidateTreadClearance=std::min(candidateTreadClearance,
clearance);
}
}
}
double previousTreadClearance=std::numeric_limits<double>::infinity();
const double entryXQ=entryX,entryYQ=entryY;
const double entryZQ=static_cast<double>(secondAxisZ-firstAxisZ);
const double entryLengthSquared=entryXQ*entryXQ+entryYQ*entryYQ
+entryZQ*entryZQ;
const double mouthStartT=std::clamp(
mouthRadius/entryLength,0.0,1.0);
for(const auto& previousPassage:layout.passages)
{
if(previousPassage.stableId==passage.stableId
||previousPassage.controlPoints.size()<2)continue;
for(std::size_t previousSegment=0;
previousSegment+1<previousPassage.controlPoints.size();
++previousSegment)
{
const auto& previousA=previousPassage.controlPoints[previousSegment];
const auto& previousB=previousPassage.controlPoints[previousSegment+1];
const double ax=static_cast<double>(previousA.positionSubvoxels[0]);
const double ay=static_cast<double>(previousA.positionSubvoxels[1]);
const double az=static_cast<double>(previousA.positionSubvoxels[2]);
const double dx=static_cast<double>(previousB.positionSubvoxels[0])-ax;
const double dy=static_cast<double>(previousB.positionSubvoxels[1])-ay;
const double dz=static_cast<double>(previousB.positionSubvoxels[2])-az;
for(int sample=0;sample<=8;++sample)
{
const double t=static_cast<double>(sample)/8.0;
const double px=ax+t*dx;
const double py=ay+t*dy;
const double pz=az+t*dz
-(previousA.radiusSubvoxels+t*(
static_cast<double>(previousB.radiusSubvoxels)
-previousA.radiusSubvoxels))
+0.20*qPerMetre;
const double projection=entryLengthSquared>0.0
?std::clamp(((px-upperMouth[0])*entryXQ
+(py-upperMouth[1])*entryYQ
+(pz-firstAxisZ)*entryZQ)/entryLengthSquared,
mouthStartT,1.0):mouthStartT;
const double ex=px-(upperMouth[0]+projection*entryXQ);
const double ey=py-(upperMouth[1]+projection*entryYQ);
const double ez=pz-(firstAxisZ+projection*entryZQ);
const double clearance=std::sqrt(ex*ex+ey*ey+ez*ez)
-mouthRadius;
previousTreadClearance=std::min(previousTreadClearance,
clearance);
}
}
}
const double clearanceScore=std::isfinite(candidateTreadClearance)
&&std::isfinite(previousTreadClearance)
?std::min(candidateTreadClearance,previousTreadClearance)/qPerMetre
:0.0;
if(phaseScoreCount<phaseScores.size())
phaseScores[phaseScoreCount++]={candidate,candidateDirection,
routeLength,clearanceScore,tangentAlignment};
}
}
if(phaseScoreCount>0)
{
double bestClearance=-std::numeric_limits<double>::infinity();
for(std::size_t index=0;index<phaseScoreCount;++index)
bestClearance=std::max(bestClearance,phaseScores[index].clearance);
constexpr double ClearanceToleranceMetres=0.10;
const FPhaseScore* selected=nullptr;
for(std::size_t index=0;index<phaseScoreCount;++index)
{
const auto& candidate=phaseScores[index];
if(candidate.clearance+ClearanceToleranceMetres<bestClearance)continue;
const bool shorter=!selected||candidate.routeLength<selected->routeLength;
const bool stableLengthTie=selected
&&candidate.routeLength==selected->routeLength
&&(candidate.clearance>selected->clearance+1.0e-6
||(std::abs(candidate.clearance-selected->clearance)<=1.0e-6
&&candidate.tangent>selected->tangent));
if(shorter||stableLengthTie)selected=&candidate;
}
if(selected)
{
phase=selected->phase;
direction=selected->direction;
clockwise=direction>0;
}
}
}
direction=clockwise?1:-1;
std::vector<std::array<std::int64_t,2>> xy;
const auto pushXY=[&](std::array<std::int64_t,2> p)
{if(xy.empty()||xy.back()!=p)xy.push_back(p);};
@@ -2731,6 +3004,12 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
}
++loops;
}
const std::int64_t mouthPlanSeparation=distanceQ(
lowerMouth[0]-upperMouth[0],lowerMouth[1]-upperMouth[1]);
const bool shortGeneratedClosure=!hasLowerTangent&&!hasAnchorHelix
&&pathStyle==1u&&loops<=1
&&mouthPlanSeparation<=2*mouthRadius
&&verticalDistance<=2*mouthRadius+qPerMetre/2;
if(hasLowerTangent)
{
std::uint32_t lowerPhase=static_cast<std::uint32_t>(PassageSeed(seed,
@@ -2789,6 +3068,48 @@ bool PlanResolvedLayoutPassagesV1(ResolvedLayoutSnapshotV3& layout,std::int32_t
}
pushXY(ringPoint(lowerPhase));
}
else if(shortGeneratedClosure)
{
// A one-turn generated route whose mouths share a plan location
// must leave and rejoin from different ring tangents. Returning on
// the entry chord creates a hairpin that overlaps its own tread.
double bestExitScore=-std::numeric_limits<double>::infinity();
std::uint32_t lowerPhase=phase;
const auto firstRing=ringPoint(phase,loops);
for(std::uint32_t candidate=0;candidate<16;++candidate)
{
if(candidate==phase)continue;
const std::uint32_t previous=static_cast<std::uint32_t>(
(static_cast<int>(candidate)-direction+16)%16);
const auto terminal=ringPoint(candidate,loops);
const auto beforeTerminal=ringPoint(previous,loops);
const double incomingX=static_cast<double>(terminal[0]-beforeTerminal[0]);
const double incomingY=static_cast<double>(terminal[1]-beforeTerminal[1]);
const double outgoingX=static_cast<double>(lowerMouth[0]-terminal[0]);
const double outgoingY=static_cast<double>(lowerMouth[1]-terminal[1]);
const double incomingLength=std::hypot(incomingX,incomingY);
const double outgoingLength=std::hypot(outgoingX,outgoingY);
if(incomingLength<=1.0e-6||outgoingLength<=1.0e-6)continue;
const double tangentAlignment=(incomingX*outgoingX+incomingY*outgoingY)
/(incomingLength*outgoingLength);
const double separation=std::hypot(
static_cast<double>(terminal[0]-firstRing[0]),
static_cast<double>(terminal[1]-firstRing[1]));
const double score=tangentAlignment
+0.20*std::clamp(separation/(2.0*qPerMetre),0.0,2.0);
if(score>bestExitScore)
{bestExitScore=score;lowerPhase=candidate;}
}
for(int step=1;step<=16;++step)
{
int directionIndex=static_cast<int>(phase)+direction*step;
directionIndex=(directionIndex%16+16)%16;
pushXY(ringPoint(static_cast<std::uint32_t>(directionIndex),loops));
if(static_cast<std::uint32_t>(directionIndex)==lowerPhase)break;
}
std::printf("[ResolvedPassageGeneratedExitApproach] id=%s turns=%u phase=%u exit_phase=%u tangent_score=%.3f status=PASS\n",
passage.stableId.c_str(),loops,phase,lowerPhase,bestExitScore);
}
pushXY({lowerMouth[0],lowerMouth[1]});totalHorizontal=measurePath();
std::size_t lowerMouthIndex=xy.size()-1;
std::vector<std::array<std::int64_t,3>> lowerLandingPoints;