diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 2ada3a6..373b28a 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -182,6 +182,10 @@ namespace HyperTwistCoachDashboardWidgetInternal { return TEXT("successful deferred tightened-launch history"); } + if (SourceLabel == TEXT("recommendation-history-deferred-first-follow-up")) + { + return TEXT("successful deferred-first follow-up packet history"); + } return SourceLabel.IsEmpty() ? TEXT("n/a") : SourceLabel; } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index b26271e..98086e8 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -25,7 +25,8 @@ namespace HyperTwistTrainingCoachLibraryInternal bool IsDeferredOverflowDashboardHandoffSource(const FString& SourceLabel) { - return SourceLabel.StartsWith(TEXT("coach-memory-dashboard-deferred-overflow-")); + return SourceLabel.StartsWith(TEXT("coach-memory-dashboard-deferred-overflow-")) + || SourceLabel == TEXT("recommendation-history-deferred-first-follow-up"); } FString DescribeCoachHandoffKind(const EHyperTwistTrainingCoachHandoffKind HandoffKind) @@ -131,6 +132,10 @@ namespace HyperTwistTrainingCoachLibraryInternal { return TEXT("successful deferred tightened-launch history"); } + if (SourceLabel == TEXT("recommendation-history-deferred-first-follow-up")) + { + return TEXT("successful deferred-first follow-up packet 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 e761edb..a244189 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp @@ -106,7 +106,47 @@ namespace HyperTwistTrainingRepositoryLibraryInternal bool IsDeferredOverflowDashboardHandoffSource(const FString& SourceLabel) { - return SourceLabel.StartsWith(TEXT("coach-memory-dashboard-deferred-overflow-")); + return SourceLabel.StartsWith(TEXT("coach-memory-dashboard-deferred-overflow-")) + || SourceLabel == TEXT("recommendation-history-deferred-first-follow-up"); + } + + bool IsDeferredFirstFollowUpPacket(const FHyperTwistTrainingCoachActionPlan& ActionPlan) + { + if (ActionPlan.FollowUpDepth <= 0 + || ActionPlan.DeferredLaunchCaseIds.Num() <= 0 + || ActionPlan.FocusCaseIds.Num() <= 0) + { + return false; + } + + TSet DeferredLaunchCaseSet; + for (const FString& CaseId : ActionPlan.DeferredLaunchCaseIds) + { + if (!CaseId.IsEmpty()) + { + DeferredLaunchCaseSet.Add(CaseId); + } + } + + if (DeferredLaunchCaseSet.Num() <= 0) + { + return false; + } + + int32 DeferredPrefixCount = 0; + for (const FString& CaseId : ActionPlan.FocusCaseIds) + { + if (DeferredLaunchCaseSet.Contains(CaseId)) + { + DeferredPrefixCount += 1; + continue; + } + + break; + } + + return DeferredPrefixCount > 0 + && DeferredPrefixCount == FMath::Min(DeferredLaunchCaseSet.Num(), ActionPlan.FocusCaseIds.Num()); } TArray DeduplicateTemplateCaseIds(const TArray& FocusCaseIds) @@ -5489,6 +5529,8 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der Snapshot.DeferredTightenedLaunchHistoryCount = RecommendationHistory.DeferredTightenedLaunchPlanCount; Snapshot.DeferredLaunchHistoryCaseCount = RecommendationHistory.DeferredLaunchCaseCount; + Snapshot.DeferredFirstFollowUpPacketHistoryCount = + RecommendationHistory.DeferredFirstFollowUpPacketPlanCount; Snapshot.bHasRepeatRecommendationPressure = RecommendationHistory.bHasRepeatRecommendationPressure; const int32 DeferredTightenedLaunchBias = FMath::Clamp( RecommendationHistory.DeferredTightenedLaunchPlanCount, @@ -5526,6 +5568,34 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der TEXT("recommendation-history-deferred-tightened-launch"); } } + if (RecommendationHistory.DeferredFirstFollowUpPacketPlanCount >= 2 + && RecommendationHistory.DeferredFirstFollowUpPacketAverageOutcomeScore + >= RecommendationHistory.DeferredTightenedLaunchAverageOutcomeScore - 3.0f) + { + const int32 DeferredFirstPacketBias = FMath::Clamp( + RecommendationHistory.DeferredFirstFollowUpPacketPlanCount, + 0, + 3 + ); + Snapshot.FollowUpGuidanceContinueBias += DeferredFirstPacketBias; + Snapshot.SuppressedPrimaryQueuePressureCount += FMath::Max(1, DeferredFirstPacketBias - 1); + if (Snapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::None + || Snapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended + || Snapshot.PreferredGuidanceSourceLabel.StartsWith(TEXT("recommendation-history"))) + { + Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::FollowUp; + Snapshot.PreferredGuidanceSourceLabel = + TEXT("recommendation-history-deferred-first-follow-up"); + } + if (Snapshot.PreferredCoachHandoffKind == EHyperTwistTrainingCoachHandoffKind::None + || Snapshot.PreferredCoachHandoffSourceLabel.IsEmpty() + || Snapshot.PreferredCoachHandoffSourceLabel.StartsWith(TEXT("recommendation-history"))) + { + Snapshot.PreferredCoachHandoffKind = EHyperTwistTrainingCoachHandoffKind::FollowUp; + Snapshot.PreferredCoachHandoffSourceLabel = + TEXT("recommendation-history-deferred-first-follow-up"); + } + } if (!RecommendationHistory.FocusDeckId.IsEmpty() && (Snapshot.FocusDeckId.IsEmpty() @@ -8939,6 +9009,7 @@ FHyperTwistTrainingCoachRecommendationHistorySummary UHyperTwistTrainingReposito float BroadenedLaunchOutcomeAccumulator = 0.0f; float TightenedLaunchOutcomeAccumulator = 0.0f; float DeferredTightenedLaunchOutcomeAccumulator = 0.0f; + float DeferredFirstFollowUpPacketOutcomeAccumulator = 0.0f; FString LatestPlanSortUtc; for (const FHyperTwistTrainingCoachActionPlan& Plan : UserPlans) @@ -9135,6 +9206,11 @@ FHyperTwistTrainingCoachRecommendationHistorySummary UHyperTwistTrainingReposito Summary.DeferredTightenedLaunchPlanCount += 1; Summary.DeferredLaunchCaseCount += OutcomeSummary.DeferredLaunchCaseIds.Num(); DeferredTightenedLaunchOutcomeAccumulator += OutcomeSummary.OutcomeScore; + if (HyperTwistTrainingRepositoryLibraryInternal::IsDeferredFirstFollowUpPacket(Pair.Value)) + { + Summary.DeferredFirstFollowUpPacketPlanCount += 1; + DeferredFirstFollowUpPacketOutcomeAccumulator += OutcomeSummary.OutcomeScore; + } } } } @@ -9250,6 +9326,12 @@ FHyperTwistTrainingCoachRecommendationHistorySummary UHyperTwistTrainingReposito DeferredTightenedLaunchOutcomeAccumulator / static_cast(Summary.DeferredTightenedLaunchPlanCount); } + if (Summary.DeferredFirstFollowUpPacketPlanCount > 0) + { + Summary.DeferredFirstFollowUpPacketAverageOutcomeScore = + DeferredFirstFollowUpPacketOutcomeAccumulator + / static_cast(Summary.DeferredFirstFollowUpPacketPlanCount); + } Summary.bHasRepeatRecommendationPressure = Summary.RepeatRecommendationSequenceCount > 0 && (Summary.RecurringCaseCount > 0 @@ -9279,6 +9361,7 @@ FHyperTwistTrainingCoachRecommendationHistorySummary UHyperTwistTrainingReposito + static_cast(Summary.FollowUpReadySequenceCount) * 2.0f + static_cast(Summary.EscalatedSequenceCount) * 1.5f + static_cast(Summary.DeferredTightenedLaunchPlanCount) * 1.5f + + static_cast(Summary.DeferredFirstFollowUpPacketPlanCount) * 1.5f + FMath::Clamp(static_cast(Summary.DeferredLaunchCaseCount) * 0.25f, 0.0f, 4.0f) + Summary.AverageFollowUpDepth; Summary.PreferredGuidanceLane = diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 02ffe06..fa80b49 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -1910,6 +1910,9 @@ struct FHyperTwistTrainingCoachMemorySnapshot UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") int32 DeferredLaunchHistoryCaseCount = 0; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DeferredFirstFollowUpPacketHistoryCount = 0; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") bool bNeedsCoachIntervention = false; @@ -3930,6 +3933,9 @@ struct FHyperTwistTrainingCoachRecommendationHistorySummary UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") int32 DeferredLaunchCaseCount = 0; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DeferredFirstFollowUpPacketPlanCount = 0; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") int32 RecurringCaseCount = 0; @@ -3957,6 +3963,9 @@ struct FHyperTwistTrainingCoachRecommendationHistorySummary UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") float DeferredTightenedLaunchAverageOutcomeScore = 0.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float DeferredFirstFollowUpPacketAverageOutcomeScore = 0.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") TArray RecurringCaseIds;