fix(measure): a blocked route is not a disconnected pair

VF_AreConnected took the shortest coarse BFS route, re-walked it at full
resolution, and on hitting a wall returned false and gave up -- never
looking for another way round. That is a false NEGATIVE, and it was
inflating the failure count of a bug we were actively trying to fix.

On refutation it now excludes that specific coarse step and re-searches,
up to a bounded budget (16). Outcomes are distinguished honestly:
  Connected                   a route survived full-resolution verification
  NotConnectedAtThisResolution no coarse route remains after exclusions
  CoarseLiedBudgetExhausted    UNKNOWN -- routes kept being refuted
The third is deliberately NOT folded into either other verdict. Collapsing
"unknown" into "disconnected" is precisely what misled us.

  arrival->departure   7/16 -> 14/16
  All 7 former COARSE_LIED became CONNECTED.
  arrival->spine   15/16 connected      departure->spine 15/16 connected
  per-archetype law: 6/6 applicable routes pass

Retries needed are small -- max 3 per query, 0 budget hits -- so 16 is
generous rather than load-bearing. Runtime slightly IMPROVED (45.3 -> 44.9 s
on the 16-seed sweep) because a satisfied query stops early.

The false-POSITIVE guard is untouched and is now stronger in meaning:
every Connected still requires a fully re-walked, air-verified route, and
it now says "this specific route is open" rather than "the first route I
tried was open". The controlled wall still returns BudgetExhausted after
exactly 16 retries, and a repeat query is bit-identical in verdict, snap
flags and retry count.

⚠️ REMAINING: seeds 6 and 14 are genuinely NOT_CONNECTED. Real or a
step-2 resolution limit is not yet settled -- refine before touching
generation again, per COMPOSER-NOTES §13.

No generation, placement or parameter changed. Verifier only.
Build clean, 20 succeeded / 0 warnings / 0 failed, confirmed in VoxelM.log.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJiTcTSDNkKBtnNdiBtdaU
This commit is contained in:
Fr0zkaandClaude Opus 5 committed 2026-09-03 23:56:25 +02:00
1 parent 271bbdd034
commit 44938f3f98
4 files changed
+343 -68

No files matched your search

+134 -15
View File
@@ -662,6 +662,41 @@ namespace VoxelStrateMeasurePrivate
return EEndpointCellResult::Air;
}
struct FCoarseEdge
{
int32 First = INDEX_NONE;
int32 Second = INDEX_NONE;
bool IsValid() const
{
return First >= 0 && Second >= 0 && First != Second;
}
};
FCoarseEdge MakeCoarseEdge(int32 A, int32 B)
{
FCoarseEdge Edge;
Edge.First = FMath::Min(A, B);
Edge.Second = FMath::Max(A, B);
return Edge;
}
bool IsCoarseEdgeBlocked(
const TArray<FCoarseEdge>& BlockedEdges,
int32 A,
int32 B)
{
const FCoarseEdge Candidate = MakeCoarseEdge(A, B);
for (const FCoarseEdge& Blocked : BlockedEdges)
{
if (Blocked.First == Candidate.First && Blocked.Second == Candidate.Second)
{
return true;
}
}
return false;
}
bool FullResolutionAirOnSegment(
const UVoxelGenerator& Generator,
const FVector& Start,
@@ -689,8 +724,11 @@ namespace VoxelStrateMeasurePrivate
const FSampleGrid& Grid,
const FVector& A,
const FVector& B,
const TArray<int32>& Path)
const TArray<int32>& Path,
FCoarseEdge& OutFailedEdge)
{
OutFailedEdge = FCoarseEdge();
int32 StartX = 0;
int32 StartY = 0;
int32 StartZ = 0;
@@ -702,6 +740,9 @@ namespace VoxelStrateMeasurePrivate
if (!FullResolutionAirOnSegment(Generator, A, Grid.CellCenter(StartX, StartY, StartZ)))
{
// The endpoint-to-cell-center segment has no coarse cell pair to exclude. It is
// common to every route from this endpoint, so report unknown rather than claiming
// that the pair is disconnected at this resolution.
return false;
}
@@ -720,17 +761,25 @@ namespace VoxelStrateMeasurePrivate
Grid.CellCenter(PreviousX, PreviousY, PreviousZ),
Grid.CellCenter(CurrentX, CurrentY, CurrentZ)))
{
OutFailedEdge = MakeCoarseEdge(Path[PathIndex - 1], Path[PathIndex]);
return false;
}
}
return FullResolutionAirOnSegment(Generator, Grid.CellCenter(EndX, EndY, EndZ), B);
if (!FullResolutionAirOnSegment(Generator, Grid.CellCenter(EndX, EndY, EndZ), B))
{
// As above, the final endpoint segment is shared by every route that reaches the
// goal cell and therefore has no precise coarse edge to exclude.
return false;
}
return true;
}
bool FindCoarsePath(
const FSampleGrid& Grid,
int32 Start,
int32 Goal,
const TArray<FCoarseEdge>& BlockedEdges,
TArray<int32>& OutPath)
{
if (Grid.Air[Start] == 0u || Grid.Air[Goal] == 0u)
@@ -762,7 +811,9 @@ namespace VoxelStrateMeasurePrivate
auto Visit = [&](int32 Neighbor)
{
if (Grid.Air[Neighbor] != 0u && Parent[Neighbor] == -2)
if (Grid.Air[Neighbor] != 0u
&& Parent[Neighbor] == -2
&& !IsCoarseEdgeBlocked(BlockedEdges, Current, Neighbor))
{
Parent[Neighbor] = Current;
Queue[Tail++] = Neighbor;
@@ -812,12 +863,15 @@ namespace VoxelStrateMeasurePrivate
int32& OutStart,
int32& OutGoal,
bool& bOutStartSnapped,
bool& bOutGoalSnapped)
bool& bOutGoalSnapped,
int32& OutNumRouteRetries,
int32 MaxRouteRetries)
{
OutStart = -1;
OutGoal = -1;
bOutStartSnapped = false;
bOutGoalSnapped = false;
OutNumRouteRetries = 0;
const EEndpointCellResult StartCell = ResolveEndpointCell(
Grid, AVoxel, OutStart, bOutStartSnapped);
@@ -837,20 +891,40 @@ namespace VoxelStrateMeasurePrivate
return EVoxelConnectivityResult::GoalCellSolid;
}
TArray<FCoarseEdge> BlockedEdges;
TArray<int32> Path;
if (!FindCoarsePath(Grid, OutStart, OutGoal, Path))
for (;;)
{
return EVoxelConnectivityResult::NotConnectedAtThisResolution;
}
if (!FindCoarsePath(Grid, OutStart, OutGoal, BlockedEdges, Path))
{
// This is a negative verdict only after all edges excluded by earlier
// full-resolution failures leave no coarse route at this resolution.
return EVoxelConnectivityResult::NotConnectedAtThisResolution;
}
// The coarse route is evidence, not a pass. Re-check this one BFS route at one-voxel
// spacing in the original voxel field. A single solid sample is the dangerous coarse
// false positive.
if (!FullResolutionPathIsAir(Generator, Grid, AVoxel, BVoxel, Path))
{
return EVoxelConnectivityResult::CoarseLied;
// The coarse route is evidence, not a pass. Re-check every candidate at one-voxel
// spacing in the original voxel field. A single solid sample refutes only this
// route, so exclude its precise coarse edge and search again.
FCoarseEdge FailedEdge;
if (FullResolutionPathIsAir(Generator, Grid, AVoxel, BVoxel, Path, FailedEdge))
{
return EVoxelConnectivityResult::Connected;
}
if (OutNumRouteRetries >= MaxRouteRetries || !FailedEdge.IsValid())
{
// A route can be refuted without a retryable pair when the failure is in an
// endpoint-to-cell segment (or when the explicit retry budget is spent). Both
// cases are unknown, never evidence of disconnection.
return EVoxelConnectivityResult::CoarseLiedBudgetExhausted;
}
check(FailedEdge.First != FailedEdge.Second);
check(!IsCoarseEdgeBlocked(
BlockedEdges, FailedEdge.First, FailedEdge.Second));
BlockedEdges.Add(FailedEdge);
++OutNumRouteRetries;
}
return EVoxelConnectivityResult::Connected;
}
float DistanceToComponent(
@@ -899,12 +973,14 @@ namespace VoxelStrateMeasurePrivate
EVoxelConnectivityResult ConnectivityResult,
bool bStartSnapped,
bool bGoalSnapped,
int32 NumRouteRetries,
FVoxelConnectivityDiagnostics& OutDiagnostics)
{
OutDiagnostics = FVoxelConnectivityDiagnostics();
OutDiagnostics.Result = ConnectivityResult;
OutDiagnostics.bStartSnapped = bStartSnapped;
OutDiagnostics.bGoalSnapped = bGoalSnapped;
OutDiagnostics.NumRouteRetries = NumRouteRetries;
OutDiagnostics.bValid = ConnectivityResult != EVoxelConnectivityResult::OutOfWindow
|| (Start >= 0 && Goal >= 0);
if (Start < 0 || Goal < 0)
@@ -983,10 +1059,12 @@ namespace VoxelStrateMeasurePrivate
const FVoxelStrateMeasureSettings& Settings,
bool& bOutStartSnapped,
bool& bOutGoalSnapped,
int32& OutNumRouteRetries,
FVoxelConnectivityDiagnostics* OutDiagnostics)
{
bOutStartSnapped = false;
bOutGoalSnapped = false;
OutNumRouteRetries = 0;
if (!FMath::IsFinite(AVoxel.X) || !FMath::IsFinite(AVoxel.Y)
|| !FMath::IsFinite(AVoxel.Z) || !FMath::IsFinite(BVoxel.X)
|| !FMath::IsFinite(BVoxel.Y) || !FMath::IsFinite(BVoxel.Z))
@@ -997,6 +1075,14 @@ namespace VoxelStrateMeasurePrivate
}
return EVoxelConnectivityResult::OutOfWindow;
}
if (Settings.MaxRouteRetries < 0)
{
if (OutDiagnostics != nullptr)
{
*OutDiagnostics = FVoxelConnectivityDiagnostics();
}
return EVoxelConnectivityResult::OutOfWindow;
}
FSampleGrid Grid;
FString RefusalReason;
@@ -1019,7 +1105,9 @@ namespace VoxelStrateMeasurePrivate
Start,
Goal,
bOutStartSnapped,
bOutGoalSnapped);
bOutGoalSnapped,
OutNumRouteRetries,
Settings.MaxRouteRetries);
if (OutDiagnostics != nullptr)
{
PopulateConnectivityDiagnostics(
@@ -1029,6 +1117,7 @@ namespace VoxelStrateMeasurePrivate
Result,
bOutStartSnapped,
bOutGoalSnapped,
OutNumRouteRetries,
*OutDiagnostics);
}
return Result;
@@ -1071,6 +1160,7 @@ EVoxelConnectivityResult VF_AreConnected(
bool& bOutStartSnapped,
bool& bOutGoalSnapped)
{
int32 NumRouteRetries = 0;
return VoxelStrateMeasurePrivate::QueryConnectivity(
Generator,
Manager,
@@ -1080,6 +1170,31 @@ EVoxelConnectivityResult VF_AreConnected(
Settings,
bOutStartSnapped,
bOutGoalSnapped,
NumRouteRetries,
nullptr);
}
EVoxelConnectivityResult VF_AreConnected(
const UVoxelGenerator& Generator,
const UVoxelStrateManager& Manager,
int32 StrateIndex,
const FVector& AVoxel,
const FVector& BVoxel,
const FVoxelStrateMeasureSettings& Settings,
bool& bOutStartSnapped,
bool& bOutGoalSnapped,
int32& OutNumRouteRetries)
{
return VoxelStrateMeasurePrivate::QueryConnectivity(
Generator,
Manager,
StrateIndex,
AVoxel,
BVoxel,
Settings,
bOutStartSnapped,
bOutGoalSnapped,
OutNumRouteRetries,
nullptr);
}
@@ -1094,6 +1209,7 @@ EVoxelConnectivityResult VF_AreConnected(
bool& bOutGoalSnapped,
FVoxelConnectivityDiagnostics& OutDiagnostics)
{
int32 NumRouteRetries = 0;
return VoxelStrateMeasurePrivate::QueryConnectivity(
Generator,
Manager,
@@ -1103,6 +1219,7 @@ EVoxelConnectivityResult VF_AreConnected(
Settings,
bOutStartSnapped,
bOutGoalSnapped,
NumRouteRetries,
&OutDiagnostics);
}
@@ -1115,6 +1232,7 @@ FVoxelConnectivityDiagnostics VF_DiagnoseConnectivity(
const FVoxelStrateMeasureSettings& Settings)
{
FVoxelConnectivityDiagnostics Result;
int32 NumRouteRetries = 0;
VoxelStrateMeasurePrivate::QueryConnectivity(
Generator,
Manager,
@@ -1124,6 +1242,7 @@ FVoxelConnectivityDiagnostics VF_DiagnoseConnectivity(
Settings,
Result.bStartSnapped,
Result.bGoalSnapped,
NumRouteRetries,
&Result);
return Result;
}