From ea38785b8124ebb73e176d5784c49087a20d1a74 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Wed, 29 Apr 2026 18:23:40 +0200 Subject: [PATCH] Compare drill recovery plans on dashboard --- .../HyperTwistCoachDashboardWidget.cpp | 179 +++++++++++++++++- 1 file changed, 175 insertions(+), 4 deletions(-) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 009044f..a68c381 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -104,6 +104,17 @@ namespace HyperTwistCoachDashboardWidgetInternal bool bHasSignal = false; }; + struct FMethodDrillRecoveryPlanComparisonSummary + { + int32 TightPlanSourceCount = 0; + int32 TightPlanAlignedCount = 0; + int32 TightPlanDivergedCount = 0; + int32 BroadPlanSourceCount = 0; + int32 BroadPlanAlignedCount = 0; + int32 BroadPlanDivergedCount = 0; + bool bHasSignal = false; + }; + template FString EnumDisplayName(const TEnum Value) { @@ -701,6 +712,30 @@ namespace HyperTwistCoachDashboardWidgetInternal return TEXT("steady"); } + bool IsTightMethodDrillRecoveryPlanSourceLabel(const FString& SourceLabel) + { + return SourceLabel.Contains(TEXT("structure-plan-tight")); + } + + bool IsBroadMethodDrillRecoveryPlanSourceLabel(const FString& SourceLabel) + { + return SourceLabel.Contains(TEXT("structure-plan-broad")); + } + + FString DescribeMethodDrillRecoveryPlanSourceLabel(const FString& SourceLabel) + { + if (IsTightMethodDrillRecoveryPlanSourceLabel(SourceLabel)) + { + return TEXT("tight retained drill-recovery plan"); + } + if (IsBroadMethodDrillRecoveryPlanSourceLabel(SourceLabel)) + { + return TEXT("broad retained drill-recovery plan"); + } + + return TEXT("none"); + } + FString DashboardHandoffSourceLabel( const EHyperTwistTrainingCoachHandoffKind HandoffKind, const bool bDeferredFirstSuppressionDriven, @@ -1613,6 +1648,101 @@ namespace HyperTwistCoachDashboardWidgetInternal return Bias; } + FMethodDrillRecoveryPlanComparisonSummary DeriveMethodDrillRecoveryPlanComparisonSummary( + const TArray& Entries, + const int32 MaxEntriesToInspect = 8) + { + FMethodDrillRecoveryPlanComparisonSummary Summary; + if (Entries.Num() <= 0) + { + return Summary; + } + + const int32 StartIndex = FMath::Max(0, Entries.Num() - MaxEntriesToInspect); + for (int32 Index = StartIndex; Index < Entries.Num(); ++Index) + { + const FHyperTwistCoachDashboardMethodDrillRecoveryMemoryHistoryEntry& Entry = Entries[Index]; + if (!Entry.IsStructurallyValid()) + { + continue; + } + + if (IsTightMethodDrillRecoveryPlanSourceLabel(Entry.PreferredRecoveryActionSourceLabel)) + { + ++Summary.TightPlanSourceCount; + Summary.bHasSignal = true; + if (Entry.bGeneratedPlanUsedTightStructureContinuationPosture) + { + ++Summary.TightPlanAlignedCount; + } + else + { + ++Summary.TightPlanDivergedCount; + } + } + else if (IsBroadMethodDrillRecoveryPlanSourceLabel( + Entry.PreferredRecoveryActionSourceLabel)) + { + ++Summary.BroadPlanSourceCount; + Summary.bHasSignal = true; + if (Entry.bGeneratedPlanUsedBroadStructureRedirectPosture) + { + ++Summary.BroadPlanAlignedCount; + } + else + { + ++Summary.BroadPlanDivergedCount; + } + } + } + + return Summary; + } + + FString DescribeMethodDrillRecoveryPlanAlignment( + const FHyperTwistCoachDashboardMethodDrillRecoveryMemoryHistoryEntry& Entry) + { + if (IsTightMethodDrillRecoveryPlanSourceLabel(Entry.PreferredRecoveryActionSourceLabel)) + { + return Entry.bGeneratedPlanUsedTightStructureContinuationPosture + ? TEXT("aligned tight continuation") + : (Entry.bGeneratedPlanUsedBroadStructureRedirectPosture + ? TEXT("diverged to broad redirect") + : TEXT("source tight, plan unresolved")); + } + if (IsBroadMethodDrillRecoveryPlanSourceLabel(Entry.PreferredRecoveryActionSourceLabel)) + { + return Entry.bGeneratedPlanUsedBroadStructureRedirectPosture + ? TEXT("aligned broad redirect") + : (Entry.bGeneratedPlanUsedTightStructureContinuationPosture + ? TEXT("diverged to tight continuation") + : TEXT("source broad, plan unresolved")); + } + + return TEXT("no plan-source label"); + } + + FString DescribeMethodDrillRecoveryPlanComparisonPreference( + const FMethodDrillRecoveryPlanComparisonSummary& Summary) + { + const int32 TightScore = Summary.TightPlanAlignedCount - Summary.TightPlanDivergedCount; + const int32 BroadScore = Summary.BroadPlanAlignedCount - Summary.BroadPlanDivergedCount; + if (TightScore > BroadScore && Summary.TightPlanSourceCount > 0) + { + return TEXT("tight continuation"); + } + if (BroadScore > TightScore && Summary.BroadPlanSourceCount > 0) + { + return TEXT("broad redirect"); + } + if (Summary.bHasSignal) + { + return TEXT("mixed"); + } + + return TEXT("none"); + } + void CollectMethodDrillRecoveryOutcomeIndices( const TArray& Outcomes, TArray& OutIndices) @@ -8115,6 +8245,11 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() HyperTwistCoachDashboardWidgetInternal::DeriveMethodDrillRecoveryPacketDashboardBias( MethodDrillRecoveryMemoryHistoryEntries ); + const HyperTwistCoachDashboardWidgetInternal::FMethodDrillRecoveryPlanComparisonSummary + MethodDrillRecoveryPlanComparisonSummary = + HyperTwistCoachDashboardWidgetInternal::DeriveMethodDrillRecoveryPlanComparisonSummary( + MethodDrillRecoveryMemoryHistoryEntries + ); const HyperTwistCoachDashboardWidgetInternal::EMethodDrillRecoveryMemoryBias EffectiveMethodDrillRecoveryMemoryHandoffBias = RetainedMethodDrillRecoveryMemoryHandoffBias @@ -8868,6 +9003,15 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() *HyperTwistCoachDashboardWidgetInternal::DescribeHandoffRecommendationKind( MethodDrillRecoveryPacketHandoffBias.PreferredHandoffKind) ); + CoachHandoffRecommendationDetailLine += FString::Printf( + TEXT(" | plan-source tight aligned/diverged %d/%d | broad aligned/diverged %d/%d | plan preferred: %s"), + MethodDrillRecoveryPlanComparisonSummary.TightPlanAlignedCount, + MethodDrillRecoveryPlanComparisonSummary.TightPlanDivergedCount, + MethodDrillRecoveryPlanComparisonSummary.BroadPlanAlignedCount, + MethodDrillRecoveryPlanComparisonSummary.BroadPlanDivergedCount, + *HyperTwistCoachDashboardWidgetInternal::DescribeMethodDrillRecoveryPlanComparisonPreference( + MethodDrillRecoveryPlanComparisonSummary) + ); CoachHandoffRecommendationDetailLine += FString::Printf( TEXT(" | strong posture q/f/r %d/%d/%d, weak posture q/f/r %d/%d/%d"), HandoffHistoryBias.StrongRetainedMethodDrillQueueAcceptedDeltaCount, @@ -9286,7 +9430,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() : *QueueDeferredFirstSuppressionSignal->Detail) : TEXT(""); const FString QueueRecoveryDetailLine = FString::Printf( - TEXT("Deferred events %d | Promoted %d | Manual %d | Pressure %.2f | Dominant reason: %s | Follow-up deferrals: %s | Archive-aware deferrals: %s | Completion gap: %s | Upstream action source: %s%s || Learned promote %d/%d reduced, %d failed, avg delta %.2f | launch %d/%d reduced, %d failed, avg delta %.2f | deferred-first promote/launch reduced %d/%d over %d/%d | drill-driven outcome promote/launch reduced %d/%d over %d/%d | retained strong-launch %d/%d, weak-promote %d/%d | drill-driven bias promote/launch %d/%d | packet history queue/follow-up/recommended %d/%d/%d | packet-plan posture tight/broad %d/%d | packet bias promote/launch %d/%d | upstream drill recovery strong/weak/history %d/%d/%d avg %.1f | retained drill-recovery stance: %s"), + TEXT("Deferred events %d | Promoted %d | Manual %d | Pressure %.2f | Dominant reason: %s | Follow-up deferrals: %s | Archive-aware deferrals: %s | Completion gap: %s | Upstream action source: %s%s || Learned promote %d/%d reduced, %d failed, avg delta %.2f | launch %d/%d reduced, %d failed, avg delta %.2f | deferred-first promote/launch reduced %d/%d over %d/%d | drill-driven outcome promote/launch reduced %d/%d over %d/%d | retained strong-launch %d/%d, weak-promote %d/%d | drill-driven bias promote/launch %d/%d | packet history queue/follow-up/recommended %d/%d/%d | packet-plan posture tight/broad %d/%d | plan-source tight aligned/diverged %d/%d | broad aligned/diverged %d/%d | plan preferred: %s | packet bias promote/launch %d/%d | upstream drill recovery strong/weak/history %d/%d/%d avg %.1f | retained drill-recovery stance: %s"), CachedCoachQueueExecutionSummary.DeferredEventCount, CachedCoachQueueExecutionSummary.PromotedEventCount, CachedCoachQueueExecutionSummary.ManualRescheduleEventCount, @@ -9326,6 +9470,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() MethodDrillRecoveryPacketQueueBias.RecommendedBriefHistoryCount, MethodDrillRecoveryPacketQueueBias.TightContinuationPlanCount, MethodDrillRecoveryPacketQueueBias.BroadRedirectPlanCount, + MethodDrillRecoveryPlanComparisonSummary.TightPlanAlignedCount, + MethodDrillRecoveryPlanComparisonSummary.TightPlanDivergedCount, + MethodDrillRecoveryPlanComparisonSummary.BroadPlanAlignedCount, + MethodDrillRecoveryPlanComparisonSummary.BroadPlanDivergedCount, + *HyperTwistCoachDashboardWidgetInternal::DescribeMethodDrillRecoveryPlanComparisonPreference( + MethodDrillRecoveryPlanComparisonSummary), MethodDrillRecoveryPacketQueueBias.PromoteRecoveryBias, MethodDrillRecoveryPacketQueueBias.LaunchRecoveryBias, QueueRecoveryWeighting.MethodDrillUpstreamStrongCount, @@ -9615,6 +9765,17 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() || SelectedMethodDrillRecoveryMemoryHistoryEntry->GeneratedPlanPostureLabel.IsEmpty() ? TEXT("n/a") : SelectedMethodDrillRecoveryMemoryHistoryEntry->GeneratedPlanPostureLabel; + const FString MethodDrillRecoveryMemoryPlanSourceLine = + SelectedMethodDrillRecoveryMemoryHistoryEntry == nullptr + || SelectedMethodDrillRecoveryMemoryHistoryEntry->PreferredRecoveryActionSourceLabel.IsEmpty() + ? TEXT("none") + : HyperTwistCoachDashboardWidgetInternal::DescribeMethodDrillRecoveryPlanSourceLabel( + SelectedMethodDrillRecoveryMemoryHistoryEntry->PreferredRecoveryActionSourceLabel); + const FString MethodDrillRecoveryMemoryPlanAlignmentLine = + SelectedMethodDrillRecoveryMemoryHistoryEntry == nullptr + ? TEXT("n/a") + : HyperTwistCoachDashboardWidgetInternal::DescribeMethodDrillRecoveryPlanAlignment( + *SelectedMethodDrillRecoveryMemoryHistoryEntry); const FString MethodDrillRecoveryMemoryGeneratedPacketStanceLine = SelectedMethodDrillRecoveryMemoryHistoryEntry == nullptr ? TEXT("n/a") @@ -9629,7 +9790,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() SelectedMethodDrillRecoveryMemoryHistoryEntry != nullptr && SelectedMethodDrillRecoveryMemoryHistoryEntry->IsStructurallyValid() ? FString::Printf( - TEXT("Drill recovery memory history: %d entries | selected %d/%d | %s | %s | Packet: %s | Stance: %s | Plan: %s | Recorded: %s"), + TEXT("Drill recovery memory history: %d entries | selected %d/%d | %s | %s | Packet: %s | Stance: %s | Plan source: %s | Plan align: %s | Plan: %s | Recorded: %s"), MethodDrillRecoveryMemoryHistoryEntries.Num(), SelectedMethodDrillRecoveryMemoryHistoryIndex + 1, MethodDrillRecoveryMemoryHistoryEntries.Num(), @@ -9638,6 +9799,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() *MethodDrillRecoveryMemoryGeneratedPacketKindLine, *HyperTwistCoachDashboardWidgetInternal::DescribeMethodDrillRecoveryMemoryBias( SelectedMethodDrillRecoveryMemoryHistoryBias), + *MethodDrillRecoveryMemoryPlanSourceLine, + *MethodDrillRecoveryMemoryPlanAlignmentLine, *MethodDrillRecoveryMemoryGeneratedPlanPostureLine, *SelectedMethodDrillRecoveryMemoryHistoryEntry->RecordedAtUtc) : TEXT("Drill recovery memory history: no retained upstream drill-recovery memory shifts recorded yet."); @@ -9645,7 +9808,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() SelectedMethodDrillRecoveryMemoryHistoryEntry != nullptr && SelectedMethodDrillRecoveryMemoryHistoryEntry->IsStructurallyValid() ? FString::Printf( - TEXT("Source: %s | Strong/weak/history %d/%d/%d avg %.1f | Structure recovery narrow %d/%d broad %d/%d | Dominant pressure: %s | Structure bias: %s | Stance: %s | Recovery memory tipped: %s | Handoff memory tipped: %s | Packet kind: %s | Packet source: %s | Packet headline: %s | Packet action: %s | Packet focus %d | Plan mode: %s | Plan selection: %s | Plan posture: %s | Tight/broad structure plan: %s/%s | Packet stance: %s | Recovery posture: %s | Handoff posture: %s"), + TEXT("Source: %s | Strong/weak/history %d/%d/%d avg %.1f | Structure recovery narrow %d/%d broad %d/%d | Dominant pressure: %s | Structure bias: %s | Stance: %s | Recovery memory tipped: %s | Handoff memory tipped: %s | Packet kind: %s | Packet source: %s | Packet headline: %s | Packet action: %s | Packet focus %d | Plan mode: %s | Plan selection: %s | Plan posture: %s | Plan source: %s | Plan alignment: %s | Plan comparison tight aligned/diverged %d/%d broad aligned/diverged %d/%d | Plan preferred: %s | Tight/broad structure plan: %s/%s | Packet stance: %s | Recovery posture: %s | Handoff posture: %s"), *MethodDrillRecoveryMemorySourceLine, SelectedMethodDrillRecoveryMemoryHistoryEntry->StrongMethodDrillRecoveryHistoryCount, SelectedMethodDrillRecoveryMemoryHistoryEntry->WeakMethodDrillRecoveryHistoryCount, @@ -9682,6 +9845,14 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() *MethodDrillRecoveryMemoryGeneratedPlanModeLine, *MethodDrillRecoveryMemoryGeneratedPlanSelectionLine, *MethodDrillRecoveryMemoryGeneratedPlanPostureLine, + *MethodDrillRecoveryMemoryPlanSourceLine, + *MethodDrillRecoveryMemoryPlanAlignmentLine, + MethodDrillRecoveryPlanComparisonSummary.TightPlanAlignedCount, + MethodDrillRecoveryPlanComparisonSummary.TightPlanDivergedCount, + MethodDrillRecoveryPlanComparisonSummary.BroadPlanAlignedCount, + MethodDrillRecoveryPlanComparisonSummary.BroadPlanDivergedCount, + *HyperTwistCoachDashboardWidgetInternal::DescribeMethodDrillRecoveryPlanComparisonPreference( + MethodDrillRecoveryPlanComparisonSummary), SelectedMethodDrillRecoveryMemoryHistoryEntry ->bGeneratedPlanUsedTightStructureContinuationPosture ? TEXT("yes") @@ -9693,7 +9864,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() *MethodDrillRecoveryMemoryGeneratedPacketStanceLine, *SelectedMethodDrillRecoveryMemoryHistoryEntry->QueueRecoveryStatusLine, *SelectedMethodDrillRecoveryMemoryHistoryEntry->HandoffRecommendationStatusLine) - : TEXT("Drill recovery memory detail: retained upstream strong-vs-weak drill-recovery pressure, resulting generated queue-entry or follow-up packet lane, and resulting recovery/handoff posture will accumulate here for later comparison."); + : TEXT("Drill recovery memory detail: retained upstream strong-vs-weak drill-recovery pressure, resulting generated packet lane, and resulting plan-source vs generated-plan alignment will accumulate here for later comparison."); const FString PreferredDeferredRecoveryEntryId = FindFirstDeferredQueueEntryId(); const bool bCanApplyQueueRecovery = !bHasLiveAttemptTimer