diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 741e5c8..c7daeaa 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -28,6 +28,13 @@ namespace HyperTwistCoachDashboardWidgetInternal Recommended }; + enum class EMethodDrillRecoveryMemoryBias : uint8 + { + None, + Strong, + Weak + }; + struct FHandoffHistoryDashboardBias { int32 QueueAcceptedDeltaCount = 0; @@ -150,6 +157,30 @@ namespace HyperTwistCoachDashboardWidgetInternal { return TEXT("accepted drill-driven recommended handoff history"); } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-strong-queued")) + { + return TEXT("accepted strong drill-recovery-memory queued handoff history"); + } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-strong-follow-up")) + { + return TEXT("accepted strong drill-recovery-memory follow-up handoff history"); + } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-strong-recommended")) + { + return TEXT("accepted strong drill-recovery-memory recommended handoff history"); + } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-weak-queued")) + { + return TEXT("accepted weak drill-recovery-memory queued handoff history"); + } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-weak-follow-up")) + { + return TEXT("accepted weak drill-recovery-memory follow-up handoff history"); + } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-weak-recommended")) + { + return TEXT("accepted weak drill-recovery-memory recommended handoff history"); + } if (SourceLabel == TEXT("coach-memory-dashboard-queued")) { return TEXT("accepted queued handoff history"); @@ -206,6 +237,30 @@ namespace HyperTwistCoachDashboardWidgetInternal { return TEXT("accepted drill-driven recommended dashboard handoff"); } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-queued")) + { + return TEXT("accepted strong drill-recovery-memory queued dashboard handoff"); + } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-follow-up")) + { + return TEXT("accepted strong drill-recovery-memory follow-up dashboard handoff"); + } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-recommended")) + { + return TEXT("accepted strong drill-recovery-memory recommended dashboard handoff"); + } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-queued")) + { + return TEXT("accepted weak drill-recovery-memory queued dashboard handoff"); + } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-follow-up")) + { + return TEXT("accepted weak drill-recovery-memory follow-up dashboard handoff"); + } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-recommended")) + { + return TEXT("accepted weak drill-recovery-memory recommended dashboard handoff"); + } if (SourceLabel == TEXT("dashboard-guidance-outcome-deferred-overflow-queued")) { return TEXT("accepted deferred-overflow queued outcome handoff"); @@ -262,6 +317,14 @@ namespace HyperTwistCoachDashboardWidgetInternal { return TEXT("weak drill-driven recovery history"); } + if (SourceLabel == TEXT("coach-memory-method-drill-recovery-memory-strong")) + { + return TEXT("successful retained drill-recovery-memory history"); + } + if (SourceLabel == TEXT("coach-memory-method-drill-recovery-memory-weak")) + { + return TEXT("weak retained drill-recovery-memory history"); + } return SourceLabel.IsEmpty() ? TEXT("n/a") : SourceLabel; } @@ -283,8 +346,38 @@ namespace HyperTwistCoachDashboardWidgetInternal FString DashboardHandoffSourceLabel( const EHyperTwistTrainingCoachHandoffKind HandoffKind, const bool bDeferredFirstSuppressionDriven, - const bool bMethodDrillDriven = false) + const bool bMethodDrillDriven = false, + const EMethodDrillRecoveryMemoryBias MethodDrillRecoveryMemoryBias = + EMethodDrillRecoveryMemoryBias::None) { + if (MethodDrillRecoveryMemoryBias == EMethodDrillRecoveryMemoryBias::Strong) + { + switch (HandoffKind) + { + case EHyperTwistTrainingCoachHandoffKind::Queue: + return TEXT("dashboard-handoff-method-drill-recovery-strong-queued"); + case EHyperTwistTrainingCoachHandoffKind::FollowUp: + return TEXT("dashboard-handoff-method-drill-recovery-strong-follow-up"); + case EHyperTwistTrainingCoachHandoffKind::Recommended: + return TEXT("dashboard-handoff-method-drill-recovery-strong-recommended"); + default: + return FString(); + } + } + if (MethodDrillRecoveryMemoryBias == EMethodDrillRecoveryMemoryBias::Weak) + { + switch (HandoffKind) + { + case EHyperTwistTrainingCoachHandoffKind::Queue: + return TEXT("dashboard-handoff-method-drill-recovery-weak-queued"); + case EHyperTwistTrainingCoachHandoffKind::FollowUp: + return TEXT("dashboard-handoff-method-drill-recovery-weak-follow-up"); + case EHyperTwistTrainingCoachHandoffKind::Recommended: + return TEXT("dashboard-handoff-method-drill-recovery-weak-recommended"); + default: + return FString(); + } + } if (bMethodDrillDriven) { switch (HandoffKind) @@ -783,6 +876,49 @@ namespace HyperTwistCoachDashboardWidgetInternal } } } + + EMethodDrillRecoveryMemoryBias DeriveRetainedMethodDrillRecoveryMemoryBias( + const TArray& Entries, + const bool bForRecovery) + { + int32 StrongCount = 0; + int32 WeakCount = 0; + for (const FHyperTwistCoachDashboardMethodDrillRecoveryMemoryHistoryEntry& Entry : Entries) + { + if (!Entry.IsStructurallyValid()) + { + continue; + } + + const bool bEntryApplies = bForRecovery + ? Entry.bUsedMethodDrillRecoveryMemoryForRecovery + : Entry.bUsedMethodDrillRecoveryMemoryForHandoff; + if (!bEntryApplies) + { + continue; + } + + if (Entry.bStrongPressureDominant) + { + StrongCount += 1; + } + else if (Entry.bWeakPressureDominant) + { + WeakCount += 1; + } + } + + if (StrongCount > WeakCount && StrongCount > 0) + { + return EMethodDrillRecoveryMemoryBias::Strong; + } + if (WeakCount > StrongCount && WeakCount > 0) + { + return EMethodDrillRecoveryMemoryBias::Weak; + } + + return EMethodDrillRecoveryMemoryBias::None; + } } void UHyperTwistCoachDashboardWidget::NativeConstruct() @@ -954,6 +1090,12 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture() && (QueueRecoveryWeighting.PreferredActionKind == FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery || (!QueueRecoveryWeighting.IsStructurallyValid() && bUpstreamPrefersLaunchRecovery)); + const HyperTwistCoachDashboardWidgetInternal::EMethodDrillRecoveryMemoryBias + RetainedMethodDrillRecoveryMemoryBias = + HyperTwistCoachDashboardWidgetInternal::DeriveRetainedMethodDrillRecoveryMemoryBias( + MethodDrillRecoveryMemoryHistoryEntries, + true + ); auto StartRunAndRefresh = [this](const FHyperTwistTrainingRunState& RunState) { @@ -996,7 +1138,11 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture() return bSucceeded; }; auto BuildRecoveryLaunchSourceLabel = - [bHasDeferredFirstSuppressionSignal, bUseMethodDrillRecoverySignal]( + [ + bHasDeferredFirstSuppressionSignal, + bUseMethodDrillRecoverySignal, + RetainedMethodDrillRecoveryMemoryBias + ]( const EHyperTwistTrainingCoachHandoffKind HandoffKind) { if (bHasDeferredFirstSuppressionSignal) @@ -1011,7 +1157,9 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture() return HyperTwistCoachDashboardWidgetInternal::DashboardHandoffSourceLabel( HandoffKind, false, - true + RetainedMethodDrillRecoveryMemoryBias + == HyperTwistCoachDashboardWidgetInternal::EMethodDrillRecoveryMemoryBias::None, + RetainedMethodDrillRecoveryMemoryBias ); } @@ -4457,6 +4605,12 @@ FHyperTwistTrainingDeck UHyperTwistCoachDashboardWidget::BuildSelectedGuidancePr FString UHyperTwistCoachDashboardWidget::ResolveDashboardHandoffStartActionSourceLabel( const EHyperTwistTrainingCoachHandoffKind HandoffKind) const { + if (!DisplayedCoachHandoffMethodDrillRecoveryMemoryStartActionSourceLabel.IsEmpty() + && DisplayedCoachHandoffRecommendationKind == HandoffKind) + { + return DisplayedCoachHandoffMethodDrillRecoveryMemoryStartActionSourceLabel; + } + const bool bUseDeferredFirstSuppressionLabel = bDisplayedCoachHandoffRecommendationUsedDeferredFirstSuppressionSignal && DisplayedCoachHandoffRecommendationKind == HandoffKind; @@ -6548,6 +6702,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() DisplayedCoachHandoffRecommendationKind = EHyperTwistTrainingCoachHandoffKind::None; bDisplayedCoachHandoffRecommendationUsedDeferredFirstSuppressionSignal = false; bDisplayedCoachHandoffRecommendationUsedMethodDrillWeighting = false; + DisplayedCoachHandoffMethodDrillRecoveryMemoryStartActionSourceLabel.Reset(); FString CoachHandoffRecommendationStatusLine; FString CoachHandoffRecommendationDetailLine; const bool bQueueReadyForHandoff = CachedCoachPanelState.bCanStartQueuedRun; @@ -6571,6 +6726,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() && CachedCoachPanelState.WeakMethodDrillRecoveryHistoryCount > CachedCoachPanelState.StrongMethodDrillRecoveryHistoryCount && CachedCoachPanelState.MethodDrillRecoveryAverageOutcomeScore <= 45.0f; + const HyperTwistCoachDashboardWidgetInternal::EMethodDrillRecoveryMemoryBias + RetainedMethodDrillRecoveryMemoryHandoffBias = + HyperTwistCoachDashboardWidgetInternal::DeriveRetainedMethodDrillRecoveryMemoryBias( + MethodDrillRecoveryMemoryHistoryEntries, + false + ); if (!bCanUseCoachHandoff) { CoachHandoffRecommendationStatusLine = @@ -6969,6 +7130,18 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() bDisplayedCoachHandoffRecommendationUsedMethodDrillWeighting = bHandoffRecommendationUsedMethodDrillWeighting || bHandoffRecommendationUsedMethodDrillRecoveryMemory; + if (bHandoffRecommendationUsedMethodDrillRecoveryMemory + && RetainedMethodDrillRecoveryMemoryHandoffBias + != HyperTwistCoachDashboardWidgetInternal::EMethodDrillRecoveryMemoryBias::None) + { + DisplayedCoachHandoffMethodDrillRecoveryMemoryStartActionSourceLabel = + HyperTwistCoachDashboardWidgetInternal::DashboardHandoffSourceLabel( + DisplayedCoachHandoffRecommendationKind, + false, + false, + RetainedMethodDrillRecoveryMemoryHandoffBias + ); + } CoachHandoffRecommendationDetailLine = FString::Printf( TEXT("Closure continuation bias %d | Closure suppression bias %d | Queue ready: %s | Follow-up ready: %s | Recommended ready: %s | Preferred lane: %s | Memory source: %s%s%s%s%s%s%s | Launch bias: %s from %s | broadened/tightened history %d/%d | deferred tightened launches %d | deferred launch cases %d%s | Handoff history preferred: %s | accepted q/f/r %d/%d/%d | deferred-overflow accepted q/f/r %d/%d/%d | deferred-first recommendation q/f/r %d/%d/%d | recommended q/f/r %d/%d/%d"), diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index 1357194..2fa34b5 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -41,14 +41,22 @@ namespace HyperTwistTrainingCoachLibraryInternal return SourceLabel.StartsWith(TEXT("coach-memory-dashboard-method-drill-")) || SourceLabel == TEXT("dashboard-handoff-method-drill-queued") || SourceLabel == TEXT("dashboard-handoff-method-drill-follow-up") - || SourceLabel == TEXT("dashboard-handoff-method-drill-recommended"); + || SourceLabel == TEXT("dashboard-handoff-method-drill-recommended") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-queued") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-follow-up") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-recommended") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-queued") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-follow-up") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-recommended"); } bool IsMethodDrillRecoveryMemorySource(const FString& SourceLabel) { return IsMethodDrillDashboardHandoffSource(SourceLabel) || SourceLabel == TEXT("coach-memory-method-drill-recovery-strong") - || SourceLabel == TEXT("coach-memory-method-drill-recovery-weak"); + || SourceLabel == TEXT("coach-memory-method-drill-recovery-weak") + || SourceLabel == TEXT("coach-memory-method-drill-recovery-memory-strong") + || SourceLabel == TEXT("coach-memory-method-drill-recovery-memory-weak"); } FString DescribeCoachHandoffKind(const EHyperTwistTrainingCoachHandoffKind HandoffKind) @@ -122,6 +130,30 @@ namespace HyperTwistTrainingCoachLibraryInternal { return TEXT("accepted drill-driven recommended handoff history"); } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-strong-queued")) + { + return TEXT("accepted strong drill-recovery-memory queued handoff history"); + } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-strong-follow-up")) + { + return TEXT("accepted strong drill-recovery-memory follow-up handoff history"); + } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-strong-recommended")) + { + return TEXT("accepted strong drill-recovery-memory recommended handoff history"); + } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-weak-queued")) + { + return TEXT("accepted weak drill-recovery-memory queued handoff history"); + } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-weak-follow-up")) + { + return TEXT("accepted weak drill-recovery-memory follow-up handoff history"); + } + if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-recovery-weak-recommended")) + { + return TEXT("accepted weak drill-recovery-memory recommended handoff history"); + } if (SourceLabel == TEXT("coach-memory-queue-follow-up")) { return TEXT("queue follow-up recovery memory"); @@ -166,6 +198,30 @@ namespace HyperTwistTrainingCoachLibraryInternal { return TEXT("accepted drill-driven recommended dashboard handoff"); } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-queued")) + { + return TEXT("accepted strong drill-recovery-memory queued dashboard handoff"); + } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-follow-up")) + { + return TEXT("accepted strong drill-recovery-memory follow-up dashboard handoff"); + } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-recommended")) + { + return TEXT("accepted strong drill-recovery-memory recommended dashboard handoff"); + } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-queued")) + { + return TEXT("accepted weak drill-recovery-memory queued dashboard handoff"); + } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-follow-up")) + { + return TEXT("accepted weak drill-recovery-memory follow-up dashboard handoff"); + } + if (SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-recommended")) + { + return TEXT("accepted weak drill-recovery-memory recommended dashboard handoff"); + } if (SourceLabel == TEXT("dashboard-guidance-outcome-deferred-overflow-queued")) { return TEXT("accepted deferred-overflow queued outcome handoff"); @@ -222,6 +278,14 @@ namespace HyperTwistTrainingCoachLibraryInternal { return TEXT("weak drill-driven recovery history"); } + if (SourceLabel == TEXT("coach-memory-method-drill-recovery-memory-strong")) + { + return TEXT("successful retained drill-recovery-memory history"); + } + if (SourceLabel == TEXT("coach-memory-method-drill-recovery-memory-weak")) + { + return TEXT("weak retained drill-recovery-memory history"); + } return SourceLabel.IsEmpty() ? TEXT("none") : SourceLabel; } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp index 7659706..632b35c 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp @@ -76,13 +76,35 @@ namespace HyperTwistTrainingRepositoryLibraryInternal return SourceLabel == TEXT("dashboard-handoff-method-drill-queued") || SourceLabel == TEXT("dashboard-handoff-method-drill-follow-up") || SourceLabel == TEXT("dashboard-handoff-method-drill-recommended") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-queued") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-follow-up") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-recommended") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-queued") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-follow-up") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-recommended") || SourceLabel.StartsWith(TEXT("coach-memory-dashboard-method-drill-")); } + bool IsStrongMethodDrillRecoveryDashboardHandoffSource(const FString& SourceLabel) + { + return SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-queued") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-follow-up") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-recommended"); + } + + bool IsWeakMethodDrillRecoveryDashboardHandoffSource(const FString& SourceLabel) + { + return SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-queued") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-follow-up") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-recommended"); + } + bool IsMethodDrillRecoveryCoachMemorySource(const FString& SourceLabel) { return SourceLabel == TEXT("coach-memory-method-drill-recovery-strong") - || SourceLabel == TEXT("coach-memory-method-drill-recovery-weak"); + || SourceLabel == TEXT("coach-memory-method-drill-recovery-weak") + || SourceLabel == TEXT("coach-memory-method-drill-recovery-memory-strong") + || SourceLabel == TEXT("coach-memory-method-drill-recovery-memory-weak"); } EHyperTwistTrainingCoachHandoffKind DashboardHandoffKindFromSourceLabel(const FString& SourceLabel) @@ -90,21 +112,27 @@ namespace HyperTwistTrainingRepositoryLibraryInternal if (SourceLabel == TEXT("dashboard-handoff-queued") || SourceLabel == TEXT("dashboard-guidance-outcome-deferred-overflow-queued") || SourceLabel == TEXT("dashboard-handoff-deferred-first-queued") - || SourceLabel == TEXT("dashboard-handoff-method-drill-queued")) + || SourceLabel == TEXT("dashboard-handoff-method-drill-queued") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-queued") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-queued")) { return EHyperTwistTrainingCoachHandoffKind::Queue; } if (SourceLabel == TEXT("dashboard-handoff-follow-up") || SourceLabel == TEXT("dashboard-guidance-outcome-deferred-overflow-follow-up") || SourceLabel == TEXT("dashboard-handoff-deferred-first-follow-up") - || SourceLabel == TEXT("dashboard-handoff-method-drill-follow-up")) + || SourceLabel == TEXT("dashboard-handoff-method-drill-follow-up") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-follow-up") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-follow-up")) { return EHyperTwistTrainingCoachHandoffKind::FollowUp; } if (SourceLabel == TEXT("dashboard-handoff-recommended") || SourceLabel == TEXT("dashboard-guidance-outcome-deferred-overflow-recommended") || SourceLabel == TEXT("dashboard-handoff-deferred-first-recommended") - || SourceLabel == TEXT("dashboard-handoff-method-drill-recommended")) + || SourceLabel == TEXT("dashboard-handoff-method-drill-recommended") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-strong-recommended") + || SourceLabel == TEXT("dashboard-handoff-method-drill-recovery-weak-recommended")) { return EHyperTwistTrainingCoachHandoffKind::Recommended; } @@ -526,6 +554,24 @@ namespace HyperTwistTrainingRepositoryLibraryInternal 0, MethodDrillRecoveryStartedCount - MethodDrillRecoveryCompletedCount ); + const int32 StrongMethodDrillRecoveryMemoryStartedCount = + QueueExecutionSummary.StartedStrongMethodDrillRecoveryMemoryHandoffEventCount; + const int32 StrongMethodDrillRecoveryMemoryCompletedCount = + QueueExecutionSummary.CompletedStrongMethodDrillRecoveryMemoryHandoffEventCount; + const int32 StrongMethodDrillRecoveryMemoryOutstandingCount = FMath::Max( + 0, + StrongMethodDrillRecoveryMemoryStartedCount + - StrongMethodDrillRecoveryMemoryCompletedCount + ); + const int32 WeakMethodDrillRecoveryMemoryStartedCount = + QueueExecutionSummary.StartedWeakMethodDrillRecoveryMemoryHandoffEventCount; + const int32 WeakMethodDrillRecoveryMemoryCompletedCount = + QueueExecutionSummary.CompletedWeakMethodDrillRecoveryMemoryHandoffEventCount; + const int32 WeakMethodDrillRecoveryMemoryOutstandingCount = FMath::Max( + 0, + WeakMethodDrillRecoveryMemoryStartedCount + - WeakMethodDrillRecoveryMemoryCompletedCount + ); if (MethodDrillRecoveryStartedCount > 0) { Snapshot.MethodDrillRecoveryHistoryCount += MethodDrillRecoveryStartedCount; @@ -559,6 +605,24 @@ namespace HyperTwistTrainingRepositoryLibraryInternal 1, 3) : 0; + const int32 RetainedMethodDrillRecoveryLaunchBias = + StrongMethodDrillRecoveryMemoryStartedCount > 0 + ? FMath::Clamp( + (StrongMethodDrillRecoveryMemoryCompletedCount * 2) + - StrongMethodDrillRecoveryMemoryStartedCount + + 1, + 1, + 3) + : 0; + const int32 RetainedMethodDrillRecoveryPromoteBias = + WeakMethodDrillRecoveryMemoryStartedCount > 0 + ? FMath::Clamp( + (WeakMethodDrillRecoveryMemoryOutstandingCount * 2) + - WeakMethodDrillRecoveryMemoryCompletedCount + + 1, + 1, + 3) + : 0; if (ExplicitQueuedHandoffSignal > 0 || ExplicitFollowUpHandoffSignal > 0 || ExplicitRecommendedHandoffSignal > 0) @@ -606,14 +670,16 @@ namespace HyperTwistTrainingRepositoryLibraryInternal + ExplicitDeferredFirstFollowUpHandoffSignal + ExplicitMethodDrillQueuedHandoffSignal + ExplicitMethodDrillFollowUpHandoffSignal - + MethodDrillRecoveryLaunchBias; + + MethodDrillRecoveryLaunchBias + + RetainedMethodDrillRecoveryLaunchBias; const int32 WeightedRecommendedRecoverySignal = ExplicitRecommendedRecoverySignal + ExplicitRecommendedHandoffSignal + ExplicitDeferredOverflowRecommendedHandoffSignal + ExplicitDeferredFirstRecommendedHandoffSignal + ExplicitMethodDrillRecommendedHandoffSignal - + MethodDrillRecoveryPromoteBias; + + MethodDrillRecoveryPromoteBias + + RetainedMethodDrillRecoveryPromoteBias; const bool bQueueHistoryPrefersFollowUp = DominantDeferredLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp || (DominantCompletedLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp @@ -630,7 +696,8 @@ namespace HyperTwistTrainingRepositoryLibraryInternal + FMath::Min(static_cast(QueueExecutionSummary.PromotedEventCount), 3.0f) + (DominantDeferredLane != EHyperTwistTrainingCoachGuidanceLane::None ? 1.5f : 0.0f) - (QueueExecutionSummary.bHasCompletionGap ? 1.0f : 0.0f) - + (static_cast(MethodDrillRecoveryPromoteBias) * 1.5f); + + (static_cast(MethodDrillRecoveryPromoteBias) * 1.5f) + + (static_cast(RetainedMethodDrillRecoveryPromoteBias) * 1.75f); const float LaunchRecoveryActionPressure = (QueueExecutionSummary.bHasCompletionGap ? 4.0f : 0.0f) + (QueueExecutionSummary.bHasBlockedFollowUpFriction ? 2.5f : 0.0f) @@ -645,7 +712,8 @@ namespace HyperTwistTrainingRepositoryLibraryInternal + static_cast(ExplicitMethodDrillQueuedHandoffSignal) + static_cast(ExplicitMethodDrillFollowUpHandoffSignal) + static_cast(ExplicitMethodDrillRecommendedHandoffSignal) * 0.5f - + (static_cast(MethodDrillRecoveryLaunchBias) * 1.75f); + + (static_cast(MethodDrillRecoveryLaunchBias) * 1.75f) + + (static_cast(RetainedMethodDrillRecoveryLaunchBias) * 2.0f); if (PromoteRecoveryActionPressure > 0.0f || LaunchRecoveryActionPressure > 0.0f) { if (PromoteRecoveryActionPressure > LaunchRecoveryActionPressure) @@ -674,6 +742,29 @@ namespace HyperTwistTrainingRepositoryLibraryInternal } } if (Snapshot.PreferredQueueRecoveryAction == EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery + && RetainedMethodDrillRecoveryLaunchBias > 0 + && (Snapshot.PreferredQueueRecoveryActionSourceLabel.IsEmpty() + || Snapshot.PreferredQueueRecoveryActionSourceLabel.StartsWith(TEXT("queue-history")) + || HyperTwistTrainingRepositoryLibraryInternal::IsMethodDrillDashboardHandoffSource( + Snapshot.PreferredQueueRecoveryActionSourceLabel) + || HyperTwistTrainingRepositoryLibraryInternal::IsMethodDrillRecoveryCoachMemorySource( + Snapshot.PreferredQueueRecoveryActionSourceLabel))) + { + Snapshot.PreferredQueueRecoveryActionSourceLabel = + TEXT("coach-memory-method-drill-recovery-memory-strong"); + } + else if (Snapshot.PreferredQueueRecoveryAction + == EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred + && RetainedMethodDrillRecoveryPromoteBias > 0 + && (Snapshot.PreferredQueueRecoveryActionSourceLabel.IsEmpty() + || Snapshot.PreferredQueueRecoveryActionSourceLabel.StartsWith(TEXT("queue-history")) + || HyperTwistTrainingRepositoryLibraryInternal::IsMethodDrillRecoveryCoachMemorySource( + Snapshot.PreferredQueueRecoveryActionSourceLabel))) + { + Snapshot.PreferredQueueRecoveryActionSourceLabel = + TEXT("coach-memory-method-drill-recovery-memory-weak"); + } + else if (Snapshot.PreferredQueueRecoveryAction == EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery && bStrongMethodDrillRecoveryHistory && (Snapshot.PreferredQueueRecoveryActionSourceLabel.IsEmpty() || Snapshot.PreferredQueueRecoveryActionSourceLabel.StartsWith(TEXT("queue-history")) @@ -716,10 +807,18 @@ namespace HyperTwistTrainingRepositoryLibraryInternal { Snapshot.FollowUpGuidanceContinueBias += MethodDrillRecoveryLaunchBias; } + if (RetainedMethodDrillRecoveryLaunchBias > 0) + { + Snapshot.FollowUpGuidanceContinueBias += RetainedMethodDrillRecoveryLaunchBias; + } if (MethodDrillRecoveryPromoteBias > 0) { Snapshot.RecommendedGuidanceContinueBias += MethodDrillRecoveryPromoteBias; } + if (RetainedMethodDrillRecoveryPromoteBias > 0) + { + Snapshot.RecommendedGuidanceContinueBias += RetainedMethodDrillRecoveryPromoteBias; + } if (bQueueHistoryPrefersFollowUp && Snapshot.PreferredQueueRecoveryAction == EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred) { @@ -6559,6 +6658,24 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der 0, MethodDrillRecoveryStartedCount - MethodDrillRecoveryCompletedCount ); + const int32 StrongMethodDrillRecoveryMemoryStartedCount = + QueueExecutionSummary.StartedStrongMethodDrillRecoveryMemoryHandoffEventCount; + const int32 StrongMethodDrillRecoveryMemoryCompletedCount = + QueueExecutionSummary.CompletedStrongMethodDrillRecoveryMemoryHandoffEventCount; + const int32 StrongMethodDrillRecoveryMemoryOutstandingCount = FMath::Max( + 0, + StrongMethodDrillRecoveryMemoryStartedCount + - StrongMethodDrillRecoveryMemoryCompletedCount + ); + const int32 WeakMethodDrillRecoveryMemoryStartedCount = + QueueExecutionSummary.StartedWeakMethodDrillRecoveryMemoryHandoffEventCount; + const int32 WeakMethodDrillRecoveryMemoryCompletedCount = + QueueExecutionSummary.CompletedWeakMethodDrillRecoveryMemoryHandoffEventCount; + const int32 WeakMethodDrillRecoveryMemoryOutstandingCount = FMath::Max( + 0, + WeakMethodDrillRecoveryMemoryStartedCount + - WeakMethodDrillRecoveryMemoryCompletedCount + ); if (MethodDrillRecoveryStartedCount > 0) { Snapshot.MethodDrillRecoveryHistoryCount += MethodDrillRecoveryStartedCount; @@ -6592,6 +6709,24 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der 1, 3) : 0; + const int32 RetainedMethodDrillRecoveryLaunchBias = + StrongMethodDrillRecoveryMemoryStartedCount > 0 + ? FMath::Clamp( + (StrongMethodDrillRecoveryMemoryCompletedCount * 2) + - StrongMethodDrillRecoveryMemoryStartedCount + + 1, + 1, + 3) + : 0; + const int32 RetainedMethodDrillRecoveryPromoteBias = + WeakMethodDrillRecoveryMemoryStartedCount > 0 + ? FMath::Clamp( + (WeakMethodDrillRecoveryMemoryOutstandingCount * 2) + - WeakMethodDrillRecoveryMemoryCompletedCount + + 1, + 1, + 3) + : 0; if (ExplicitQueuedHandoffSignal > 0 || ExplicitFollowUpHandoffSignal > 0 || ExplicitRecommendedHandoffSignal > 0) @@ -6639,14 +6774,16 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der + ExplicitDeferredFirstFollowUpHandoffSignal + ExplicitMethodDrillQueuedHandoffSignal + ExplicitMethodDrillFollowUpHandoffSignal - + MethodDrillRecoveryLaunchBias; + + MethodDrillRecoveryLaunchBias + + RetainedMethodDrillRecoveryLaunchBias; const int32 WeightedRecommendedRecoverySignal = ExplicitRecommendedRecoverySignal + ExplicitRecommendedHandoffSignal + ExplicitDeferredOverflowRecommendedHandoffSignal + ExplicitDeferredFirstRecommendedHandoffSignal + ExplicitMethodDrillRecommendedHandoffSignal - + MethodDrillRecoveryPromoteBias; + + MethodDrillRecoveryPromoteBias + + RetainedMethodDrillRecoveryPromoteBias; const bool bQueueHistoryPrefersFollowUp = DominantDeferredLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp || (DominantCompletedLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp @@ -6663,7 +6800,8 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der + FMath::Min(static_cast(QueueExecutionSummary.PromotedEventCount), 3.0f) + (DominantDeferredLane != EHyperTwistTrainingCoachGuidanceLane::None ? 1.5f : 0.0f) - (QueueExecutionSummary.bHasCompletionGap ? 1.0f : 0.0f) - + (static_cast(MethodDrillRecoveryPromoteBias) * 1.5f); + + (static_cast(MethodDrillRecoveryPromoteBias) * 1.5f) + + (static_cast(RetainedMethodDrillRecoveryPromoteBias) * 1.75f); const float LaunchRecoveryActionPressure = (QueueExecutionSummary.bHasCompletionGap ? 4.0f : 0.0f) + (QueueExecutionSummary.bHasBlockedFollowUpFriction ? 2.5f : 0.0f) @@ -6678,7 +6816,8 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der + static_cast(ExplicitMethodDrillQueuedHandoffSignal) + static_cast(ExplicitMethodDrillFollowUpHandoffSignal) + static_cast(ExplicitMethodDrillRecommendedHandoffSignal) * 0.5f - + (static_cast(MethodDrillRecoveryLaunchBias) * 1.75f); + + (static_cast(MethodDrillRecoveryLaunchBias) * 1.75f) + + (static_cast(RetainedMethodDrillRecoveryLaunchBias) * 2.0f); if (PromoteRecoveryActionPressure > 0.0f || LaunchRecoveryActionPressure > 0.0f) { if (PromoteRecoveryActionPressure > LaunchRecoveryActionPressure) @@ -6707,6 +6846,29 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der } } if (Snapshot.PreferredQueueRecoveryAction == EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery + && RetainedMethodDrillRecoveryLaunchBias > 0 + && (Snapshot.PreferredQueueRecoveryActionSourceLabel.IsEmpty() + || Snapshot.PreferredQueueRecoveryActionSourceLabel.StartsWith(TEXT("queue-history")) + || HyperTwistTrainingRepositoryLibraryInternal::IsMethodDrillDashboardHandoffSource( + Snapshot.PreferredQueueRecoveryActionSourceLabel) + || HyperTwistTrainingRepositoryLibraryInternal::IsMethodDrillRecoveryCoachMemorySource( + Snapshot.PreferredQueueRecoveryActionSourceLabel))) + { + Snapshot.PreferredQueueRecoveryActionSourceLabel = + TEXT("coach-memory-method-drill-recovery-memory-strong"); + } + else if (Snapshot.PreferredQueueRecoveryAction + == EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred + && RetainedMethodDrillRecoveryPromoteBias > 0 + && (Snapshot.PreferredQueueRecoveryActionSourceLabel.IsEmpty() + || Snapshot.PreferredQueueRecoveryActionSourceLabel.StartsWith(TEXT("queue-history")) + || HyperTwistTrainingRepositoryLibraryInternal::IsMethodDrillRecoveryCoachMemorySource( + Snapshot.PreferredQueueRecoveryActionSourceLabel))) + { + Snapshot.PreferredQueueRecoveryActionSourceLabel = + TEXT("coach-memory-method-drill-recovery-memory-weak"); + } + else if (Snapshot.PreferredQueueRecoveryAction == EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery && bStrongMethodDrillRecoveryHistory && (Snapshot.PreferredQueueRecoveryActionSourceLabel.IsEmpty() || Snapshot.PreferredQueueRecoveryActionSourceLabel.StartsWith(TEXT("queue-history")) @@ -6749,10 +6911,18 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der { Snapshot.FollowUpGuidanceContinueBias += MethodDrillRecoveryLaunchBias; } + if (RetainedMethodDrillRecoveryLaunchBias > 0) + { + Snapshot.FollowUpGuidanceContinueBias += RetainedMethodDrillRecoveryLaunchBias; + } if (MethodDrillRecoveryPromoteBias > 0) { Snapshot.RecommendedGuidanceContinueBias += MethodDrillRecoveryPromoteBias; } + if (RetainedMethodDrillRecoveryPromoteBias > 0) + { + Snapshot.RecommendedGuidanceContinueBias += RetainedMethodDrillRecoveryPromoteBias; + } if (bQueueHistoryPrefersFollowUp && Snapshot.PreferredQueueRecoveryAction == EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred) { @@ -8541,6 +8711,12 @@ FHyperTwistTrainingCoachSessionQueueExecutionSummary UHyperTwistTrainingReposito const bool bMethodDrillDashboardHandoff = HyperTwistTrainingRepositoryLibraryInternal::IsMethodDrillDashboardHandoffSource( HistoryEntry.StartActionSourceLabel); + const bool bStrongMethodDrillRecoveryDashboardHandoff = + HyperTwistTrainingRepositoryLibraryInternal::IsStrongMethodDrillRecoveryDashboardHandoffSource( + HistoryEntry.StartActionSourceLabel); + const bool bWeakMethodDrillRecoveryDashboardHandoff = + HyperTwistTrainingRepositoryLibraryInternal::IsWeakMethodDrillRecoveryDashboardHandoffSource( + HistoryEntry.StartActionSourceLabel); if (DashboardHandoffKind == EHyperTwistTrainingCoachHandoffKind::Queue) { Summary.StartedQueuedHandoffEventCount += 1; @@ -8589,6 +8765,14 @@ FHyperTwistTrainingCoachSessionQueueExecutionSummary UHyperTwistTrainingReposito Summary.StartedMethodDrillRecommendedHandoffEventCount += 1; } } + if (bStrongMethodDrillRecoveryDashboardHandoff) + { + Summary.StartedStrongMethodDrillRecoveryMemoryHandoffEventCount += 1; + } + if (bWeakMethodDrillRecoveryDashboardHandoff) + { + Summary.StartedWeakMethodDrillRecoveryMemoryHandoffEventCount += 1; + } if (Summary.LastStartedAtUtc.IsEmpty() || HyperTwistTrainingRepositoryLibraryInternal::CompareUtcStrings( HistoryEntry.EventAtUtc, @@ -8639,6 +8823,12 @@ FHyperTwistTrainingCoachSessionQueueExecutionSummary UHyperTwistTrainingReposito if (HyperTwistTrainingRepositoryLibraryInternal::IsMethodDrillDashboardHandoffSource( HistoryEntry.StartActionSourceLabel)) { + const bool bStrongMethodDrillRecoveryDashboardHandoff = + HyperTwistTrainingRepositoryLibraryInternal::IsStrongMethodDrillRecoveryDashboardHandoffSource( + HistoryEntry.StartActionSourceLabel); + const bool bWeakMethodDrillRecoveryDashboardHandoff = + HyperTwistTrainingRepositoryLibraryInternal::IsWeakMethodDrillRecoveryDashboardHandoffSource( + HistoryEntry.StartActionSourceLabel); const EHyperTwistTrainingCoachHandoffKind CompletedHandoffKind = HyperTwistTrainingRepositoryLibraryInternal::DashboardHandoffKindFromSourceLabel( HistoryEntry.StartActionSourceLabel); @@ -8654,6 +8844,14 @@ FHyperTwistTrainingCoachSessionQueueExecutionSummary UHyperTwistTrainingReposito { Summary.CompletedMethodDrillRecommendedHandoffEventCount += 1; } + if (bStrongMethodDrillRecoveryDashboardHandoff) + { + Summary.CompletedStrongMethodDrillRecoveryMemoryHandoffEventCount += 1; + } + if (bWeakMethodDrillRecoveryDashboardHandoff) + { + Summary.CompletedWeakMethodDrillRecoveryMemoryHandoffEventCount += 1; + } } CompletedCountsByEntryId.FindOrAdd(HistoryEntry.EntryId) += 1; if (Summary.LastCompletedAtUtc.IsEmpty() diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 7f35f18..bbeb7d8 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -1448,6 +1448,7 @@ private: EHyperTwistTrainingCoachHandoffKind::None; bool bDisplayedCoachHandoffRecommendationUsedDeferredFirstSuppressionSignal = false; bool bDisplayedCoachHandoffRecommendationUsedMethodDrillWeighting = false; + FString DisplayedCoachHandoffMethodDrillRecoveryMemoryStartActionSourceLabel; EHyperTwistCoachDashboardGuidancePreviewLane ActiveGuidancePreviewLane = EHyperTwistCoachDashboardGuidancePreviewLane::None; int32 SelectedGuidancePreviewCaseIndex = INDEX_NONE; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index c82d3a1..229c036 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -3678,6 +3678,18 @@ struct FHyperTwistTrainingCoachSessionQueueExecutionSummary UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") int32 CompletedMethodDrillRecommendedHandoffEventCount = 0; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 StartedStrongMethodDrillRecoveryMemoryHandoffEventCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CompletedStrongMethodDrillRecoveryMemoryHandoffEventCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 StartedWeakMethodDrillRecoveryMemoryHandoffEventCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CompletedWeakMethodDrillRecoveryMemoryHandoffEventCount = 0; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") int32 PromotedFollowUpEventCount = 0;