diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index 17e7ba5..adbe0b6 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -2,6 +2,8 @@ namespace HyperTwistTrainingCoachLibraryInternal { + EHyperTwistCoachPriority PriorityFromScore(const float Score); + EHyperTwistTrainingCoachGuidanceLane GuidanceLaneFromSourceLabel(const FString& SourceLabel) { if (SourceLabel == TEXT("primary-brief")) @@ -16,6 +18,36 @@ namespace HyperTwistTrainingCoachLibraryInternal return EHyperTwistTrainingCoachGuidanceLane::None; } + float ResolvePreferredGuidanceLanePressure( + const FHyperTwistTrainingCoachMemorySnapshot& CoachMemorySnapshot) + { + if (CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::None) + { + return 0.0f; + } + + if (CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp) + { + return FMath::Clamp( + 4.0f + + static_cast(CoachMemorySnapshot.FollowUpGuidanceContinueBias) + + (static_cast(CoachMemorySnapshot.FollowUpReadyCoachSequenceCount) * 1.5f) + + (static_cast(CoachMemorySnapshot.ScheduleBlockedFollowUpEventCount) * 2.0f), + 0.0f, + 12.0f + ); + } + + return FMath::Clamp( + 4.0f + + static_cast(CoachMemorySnapshot.RecommendedGuidanceContinueBias) + + (static_cast(CoachMemorySnapshot.RepeatRecommendationSequenceCount) * 1.5f) + + (CoachMemorySnapshot.bHasRepeatRecommendationPressure ? 4.0f : 0.0f), + 0.0f, + 12.0f + ); + } + float ResolvePreferredGuidanceQueueBias( const FString& SourceLabel, const FHyperTwistTrainingCoachMemorySnapshot& CoachMemorySnapshot, @@ -32,25 +64,7 @@ namespace HyperTwistTrainingCoachLibraryInternal return 0.0f; } - float LanePressure = 0.0f; - if (CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp) - { - LanePressure = - 4.0f - + static_cast(CoachMemorySnapshot.FollowUpGuidanceContinueBias) - + (static_cast(CoachMemorySnapshot.FollowUpReadyCoachSequenceCount) * 1.5f) - + (static_cast(CoachMemorySnapshot.ScheduleBlockedFollowUpEventCount) * 2.0f); - } - else - { - LanePressure = - 4.0f - + static_cast(CoachMemorySnapshot.RecommendedGuidanceContinueBias) - + (static_cast(CoachMemorySnapshot.RepeatRecommendationSequenceCount) * 1.5f) - + (CoachMemorySnapshot.bHasRepeatRecommendationPressure ? 4.0f : 0.0f); - } - - const float ClampedBoost = FMath::Clamp(LanePressure, 0.0f, 12.0f); + const float ClampedBoost = ResolvePreferredGuidanceLanePressure(CoachMemorySnapshot); if (EntryLane == CoachMemorySnapshot.PreferredGuidanceLane) { return ClampedBoost; @@ -61,6 +75,53 @@ namespace HyperTwistTrainingCoachLibraryInternal : 0.0f; } + float ResolvePreferredGuidanceSignalBias( + const EHyperTwistTrainingCoachGuidanceLane SignalLane, + const FHyperTwistTrainingCoachMemorySnapshot& CoachMemorySnapshot, + const bool bHasCompetingGuidancePressure) + { + if (CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::None + || SignalLane == EHyperTwistTrainingCoachGuidanceLane::None) + { + return 0.0f; + } + + const float ClampedBoost = FMath::Clamp( + ResolvePreferredGuidanceLanePressure(CoachMemorySnapshot), + 0.0f, + 8.0f + ); + if (SignalLane == CoachMemorySnapshot.PreferredGuidanceLane) + { + return ClampedBoost; + } + + return bHasCompetingGuidancePressure + ? -FMath::Clamp(ClampedBoost * 0.35f, 0.0f, 3.0f) + : 0.0f; + } + + void ApplyGuidancePreferenceToSignal( + FHyperTwistCoachSignal& Signal, + const EHyperTwistTrainingCoachGuidanceLane SignalLane, + const FHyperTwistTrainingCoachMemorySnapshot& CoachMemorySnapshot, + const bool bHasCompetingGuidancePressure) + { + const float ScoreBias = ResolvePreferredGuidanceSignalBias( + SignalLane, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); + if (FMath::IsNearlyZero(ScoreBias)) + { + return; + } + + Signal.Score = FMath::Clamp(Signal.Score + ScoreBias, 0.0f, 100.0f); + Signal.Priority = PriorityFromScore(Signal.Score); + Signal.bNeedsImmediateAttention = Signal.Priority == EHyperTwistCoachPriority::Critical; + } + EHyperTwistCoachPriority PriorityFromScore(const float Score) { if (Score >= 85.0f) @@ -118,6 +179,19 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna const FHyperTwistRecognitionReplaySummary& RecognitionReplaySummary) { TArray Signals; + const bool bHasRecommendedGuidancePressure = + CoachMemorySnapshot.bHasRepeatRecommendationPressure + || CoachMemorySnapshot.RepeatRecommendationSequenceCount > 0 + || CoachMemorySnapshot.RecommendedGuidanceContinueBias > 0 + || CoachMemorySnapshot.RecurringCoachCaseIds.Num() > 0; + const bool bHasFollowUpGuidancePressure = + CoachMemorySnapshot.FollowUpReadyCoachSequenceCount > 0 + || CoachMemorySnapshot.ScheduleBlockedFollowUpEventCount > 0 + || CoachMemorySnapshot.FollowUpGuidanceContinueBias > 0 + || CoachMemorySnapshot.EscalatedCoachSequenceCount > 0 + || CoachMemorySnapshot.EscalationCaseIds.Num() > 0; + const bool bHasCompetingGuidancePressure = + bHasRecommendedGuidancePressure && bHasFollowUpGuidancePressure; if (CoachMemorySnapshot.EscalatedCoachSequenceCount > 0 || CoachMemorySnapshot.EscalationCaseIds.Num() > 0) @@ -142,6 +216,12 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna Signal.SuggestedCaseIds = CoachMemorySnapshot.EscalationCaseIds.Num() > 0 ? CoachMemorySnapshot.EscalationCaseIds : CoachMemorySnapshot.PersistentCoachWeakCaseIds; + HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal( + Signal, + EHyperTwistTrainingCoachGuidanceLane::FollowUp, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); Signals.Add(Signal); } @@ -168,6 +248,12 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna Signal.SuggestedCaseIds = CoachMemorySnapshot.PinnedCarryForwardCaseIds.Num() > 0 ? CoachMemorySnapshot.PinnedCarryForwardCaseIds : CoachMemorySnapshot.RecurringCoachCaseIds; + HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal( + Signal, + EHyperTwistTrainingCoachGuidanceLane::Recommended, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); Signals.Add(Signal); } @@ -245,6 +331,14 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna Signal.SuggestedCaseIds = CoachMemorySnapshot.SchedulePressureCaseIds.Num() > 0 ? CoachMemorySnapshot.SchedulePressureCaseIds : CoachMemorySnapshot.WeakCaseIds; + HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal( + Signal, + bBlockedFollowUp + ? EHyperTwistTrainingCoachGuidanceLane::FollowUp + : EHyperTwistTrainingCoachGuidanceLane::None, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); Signals.Add(Signal); } @@ -276,6 +370,12 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna Signal.SuggestedCaseIds = CoachMemorySnapshot.OverdueCaseIds.Num() > 0 ? CoachMemorySnapshot.OverdueCaseIds : CoachMemorySnapshot.DueCaseIds; + HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal( + Signal, + EHyperTwistTrainingCoachGuidanceLane::FollowUp, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); Signals.Add(Signal); } @@ -296,6 +396,12 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna Signal.SuggestedDeckId = CoachMemorySnapshot.FocusDeckId; Signal.SuggestedMethodSegmentId = CoachMemorySnapshot.FocusMethodSegmentId; Signal.SuggestedCaseIds = CoachMemorySnapshot.WeakCaseIds; + HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal( + Signal, + EHyperTwistTrainingCoachGuidanceLane::Recommended, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); Signals.Add(Signal); } @@ -320,6 +426,12 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna { Signal.SuggestedMethodSegmentId = CoachMemorySnapshot.RecoveryMethodSegmentIds[0]; } + HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal( + Signal, + EHyperTwistTrainingCoachGuidanceLane::Recommended, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); Signals.Add(Signal); } @@ -351,6 +463,12 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna Signal.SuggestedDeckId = CoachMemorySnapshot.FocusDeckId; Signal.SuggestedMethodSegmentId = CoachMemorySnapshot.FocusMethodSegmentId; Signal.SuggestedCaseIds = CoachMemorySnapshot.WeakCaseIds; + HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal( + Signal, + EHyperTwistTrainingCoachGuidanceLane::Recommended, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); Signals.Add(Signal); } @@ -378,6 +496,12 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna EHyperTwistTrainingSelectionPolicy::Weighted); Signal.SuggestedDeckId = SessionSummary.DeckId; Signal.SuggestedCaseIds = CoachMemorySnapshot.WeakCaseIds; + HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal( + Signal, + EHyperTwistTrainingCoachGuidanceLane::Recommended, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); Signals.Add(Signal); } @@ -404,6 +528,12 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna EHyperTwistTrainingSelectionPolicy::Weighted); Signal.SuggestedDeckId = SessionSummary.DeckId; Signal.SuggestedCaseIds = CoachMemorySnapshot.WeakCaseIds; + HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal( + Signal, + EHyperTwistTrainingCoachGuidanceLane::Recommended, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); Signals.Add(Signal); } @@ -428,6 +558,12 @@ TArray UHyperTwistTrainingCoachLibrary::DeriveCoachSigna EHyperTwistTrainingSelectionPolicy::Spaced); Signal.SuggestedDeckId = SessionSummary.DeckId; Signal.SuggestedCaseIds = CoachMemorySnapshot.DueCaseIds; + HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal( + Signal, + EHyperTwistTrainingCoachGuidanceLane::Recommended, + CoachMemorySnapshot, + bHasCompetingGuidancePressure + ); Signals.Add(Signal); }