diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index eeda4c1..a4ad8cb 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -97,7 +97,8 @@ namespace HyperTwistTrainingCoachLibraryInternal || SourceLabel == TEXT("dashboard-handoff-method-drill-follow-up-packet-strong-queued") || SourceLabel == TEXT("dashboard-handoff-method-drill-follow-up-packet-strong-follow-up") || SourceLabel == TEXT("dashboard-handoff-method-drill-follow-up-packet-strong-recommended") - || SourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-strong"); + || SourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-strong") + || SourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-structure-narrow"); } bool IsWeakMethodDrillFollowUpPacketMemorySource(const FString& SourceLabel) @@ -106,7 +107,8 @@ namespace HyperTwistTrainingCoachLibraryInternal || SourceLabel == TEXT("dashboard-handoff-method-drill-follow-up-packet-weak-queued") || SourceLabel == TEXT("dashboard-handoff-method-drill-follow-up-packet-weak-follow-up") || SourceLabel == TEXT("dashboard-handoff-method-drill-follow-up-packet-weak-recommended") - || SourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-weak"); + || SourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-weak") + || SourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-structure-broad"); } bool IsStrongMethodDrillRecoveryMemorySource(const FString& SourceLabel) @@ -556,6 +558,10 @@ namespace HyperTwistTrainingCoachLibraryInternal { return TEXT("successful drill-follow-up-packet history"); } + if (SourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-structure-narrow")) + { + return TEXT("successful narrow drill-follow-up-packet structure history"); + } if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-follow-up-packet-strong-queued")) { return TEXT("successful queued drill-follow-up-packet history"); @@ -576,6 +582,10 @@ namespace HyperTwistTrainingCoachLibraryInternal { return TEXT("weak drill-follow-up-packet history"); } + if (SourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-structure-broad")) + { + return TEXT("successful broad drill-follow-up-packet redirect history"); + } if (SourceLabel == TEXT("coach-memory-dashboard-method-drill-follow-up-packet-weak-queued")) { return TEXT("weak queued drill-follow-up-packet history"); @@ -753,7 +763,9 @@ namespace HyperTwistTrainingCoachLibraryInternal CoachMemorySnapshot.PreferredGuidanceSourceLabel == TEXT("coach-memory-method-drill-follow-up-strong") || CoachMemorySnapshot.PreferredGuidanceSourceLabel - == TEXT("coach-memory-method-drill-follow-up-packet-strong"); + == TEXT("coach-memory-method-drill-follow-up-packet-strong") + || CoachMemorySnapshot.PreferredGuidanceSourceLabel + == TEXT("coach-memory-method-drill-follow-up-packet-structure-narrow"); const bool bPreferredHandoffSource = CoachMemorySnapshot.PreferredCoachHandoffSourceLabel == TEXT("coach-memory-method-drill-follow-up-strong") @@ -792,7 +804,9 @@ namespace HyperTwistTrainingCoachLibraryInternal CoachMemorySnapshot.PreferredGuidanceSourceLabel == TEXT("coach-memory-method-drill-follow-up-weak") || CoachMemorySnapshot.PreferredGuidanceSourceLabel - == TEXT("coach-memory-method-drill-follow-up-packet-weak"); + == TEXT("coach-memory-method-drill-follow-up-packet-weak") + || CoachMemorySnapshot.PreferredGuidanceSourceLabel + == TEXT("coach-memory-method-drill-follow-up-packet-structure-broad"); const bool bPreferredHandoffSource = CoachMemorySnapshot.PreferredCoachHandoffSourceLabel == TEXT("coach-memory-method-drill-follow-up-weak") diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp index 4759051..d72e321 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp @@ -448,6 +448,18 @@ namespace HyperTwistTrainingRepositoryLibraryInternal && TrainingSessionId.Contains(TEXT("method_drill_follow_up"), ESearchCase::IgnoreCase); } + bool IsNarrowMethodDrillFollowUpPacketStructureSessionId(const FString& TrainingSessionId) + { + return !TrainingSessionId.IsEmpty() + && TrainingSessionId.Contains(TEXT("__packet_narrow_"), ESearchCase::IgnoreCase); + } + + bool IsBroadMethodDrillFollowUpPacketStructureSessionId(const FString& TrainingSessionId) + { + return !TrainingSessionId.IsEmpty() + && TrainingSessionId.Contains(TEXT("__packet_broad_"), ESearchCase::IgnoreCase); + } + float ResolveMethodDrillFollowUpOutcomeScore(const FHyperTwistTrainingRunRecord& RunRecord) { if (!RunRecord.IsStructurallyValid()) @@ -6651,6 +6663,8 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der MethodDrillFollowUpRunRecords); const int32 FirstRelevantIndex = FMath::Max(0, MethodDrillFollowUpRunRecords.Num() - 8); float MethodDrillOutcomeAccumulator = 0.0f; + float NarrowMethodDrillPacketOutcomeAccumulator = 0.0f; + float BroadMethodDrillPacketOutcomeAccumulator = 0.0f; for (int32 Index = FirstRelevantIndex; Index < MethodDrillFollowUpRunRecords.Num(); ++Index) { const FHyperTwistTrainingRunRecord& RunRecord = MethodDrillFollowUpRunRecords[Index]; @@ -6659,6 +6673,18 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der RunRecord); Snapshot.MethodDrillFollowUpHistoryCount += 1; MethodDrillOutcomeAccumulator += OutcomeScore; + if (HyperTwistTrainingRepositoryLibraryInternal::IsNarrowMethodDrillFollowUpPacketStructureSessionId( + RunRecord.Session.TrainingSessionId)) + { + Snapshot.NarrowMethodDrillFollowUpPacketStructureHistoryCount += 1; + NarrowMethodDrillPacketOutcomeAccumulator += OutcomeScore; + } + else if (HyperTwistTrainingRepositoryLibraryInternal::IsBroadMethodDrillFollowUpPacketStructureSessionId( + RunRecord.Session.TrainingSessionId)) + { + Snapshot.BroadMethodDrillFollowUpPacketStructureHistoryCount += 1; + BroadMethodDrillPacketOutcomeAccumulator += OutcomeScore; + } if (OutcomeScore >= 70.0f) { Snapshot.StrongMethodDrillFollowUpHistoryCount += 1; @@ -6675,6 +6701,18 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der MethodDrillOutcomeAccumulator / static_cast(Snapshot.MethodDrillFollowUpHistoryCount); } + if (Snapshot.NarrowMethodDrillFollowUpPacketStructureHistoryCount > 0) + { + Snapshot.NarrowMethodDrillFollowUpPacketStructureAverageOutcomeScore = + NarrowMethodDrillPacketOutcomeAccumulator + / static_cast(Snapshot.NarrowMethodDrillFollowUpPacketStructureHistoryCount); + } + if (Snapshot.BroadMethodDrillFollowUpPacketStructureHistoryCount > 0) + { + Snapshot.BroadMethodDrillFollowUpPacketStructureAverageOutcomeScore = + BroadMethodDrillPacketOutcomeAccumulator + / static_cast(Snapshot.BroadMethodDrillFollowUpPacketStructureHistoryCount); + } const bool bStrongMethodDrillHistory = Snapshot.MethodDrillFollowUpHistoryCount >= 2 @@ -6720,6 +6758,67 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der TEXT("coach-memory-method-drill-follow-up-strong"); } } + const bool bNarrowMethodDrillPacketStructureHistory = + Snapshot.NarrowMethodDrillFollowUpPacketStructureHistoryCount >= 2 + && Snapshot.NarrowMethodDrillFollowUpPacketStructureAverageOutcomeScore >= 60.0f + && (Snapshot.BroadMethodDrillFollowUpPacketStructureHistoryCount <= 0 + || Snapshot.NarrowMethodDrillFollowUpPacketStructureAverageOutcomeScore + >= Snapshot.BroadMethodDrillFollowUpPacketStructureAverageOutcomeScore); + const bool bBroadMethodDrillPacketStructureHistory = + Snapshot.BroadMethodDrillFollowUpPacketStructureHistoryCount >= 2 + && Snapshot.BroadMethodDrillFollowUpPacketStructureAverageOutcomeScore >= 58.0f + && (Snapshot.NarrowMethodDrillFollowUpPacketStructureHistoryCount <= 0 + || Snapshot.BroadMethodDrillFollowUpPacketStructureAverageOutcomeScore + >= Snapshot.NarrowMethodDrillFollowUpPacketStructureAverageOutcomeScore); + if (bNarrowMethodDrillPacketStructureHistory) + { + const int32 NarrowPacketStructureBias = FMath::Clamp( + Snapshot.NarrowMethodDrillFollowUpPacketStructureHistoryCount + - Snapshot.BroadMethodDrillFollowUpPacketStructureHistoryCount + + (Snapshot.NarrowMethodDrillFollowUpPacketStructureAverageOutcomeScore >= 75.0f + ? 1 + : 0), + 1, + 3 + ); + Snapshot.FollowUpGuidanceContinueBias += NarrowPacketStructureBias; + Snapshot.SuppressedPrimaryQueuePressureCount += FMath::Max( + 1, + NarrowPacketStructureBias - 1 + ); + if (Snapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::None + || Snapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended + || Snapshot.PreferredGuidanceSourceLabel.StartsWith(TEXT("recommendation-history")) + || Snapshot.PreferredGuidanceSourceLabel.StartsWith(TEXT("coach-memory-method-drill"))) + { + Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::FollowUp; + Snapshot.PreferredGuidanceSourceLabel = + TEXT("coach-memory-method-drill-follow-up-packet-structure-narrow"); + } + } + else if (bBroadMethodDrillPacketStructureHistory) + { + const int32 BroadPacketStructureBias = FMath::Clamp( + Snapshot.BroadMethodDrillFollowUpPacketStructureHistoryCount + - Snapshot.NarrowMethodDrillFollowUpPacketStructureHistoryCount + + (Snapshot.BroadMethodDrillFollowUpPacketStructureAverageOutcomeScore >= 72.0f + ? 1 + : 0), + 1, + 3 + ); + Snapshot.RecommendedGuidanceContinueBias += BroadPacketStructureBias; + Snapshot.SuppressedFollowUpQueuePressureCount += BroadPacketStructureBias; + if (Snapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::None + || Snapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp + || Snapshot.PreferredGuidanceSourceLabel.StartsWith(TEXT("coach-memory-method-drill"))) + { + Snapshot.PreferredGuidanceLane = + EHyperTwistTrainingCoachGuidanceLane::Recommended; + Snapshot.PreferredGuidanceSourceLabel = + TEXT("coach-memory-method-drill-follow-up-packet-structure-broad"); + } + } else if (bWeakMethodDrillHistory) { const int32 MethodDrillRedirectBias = FMath::Clamp( diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index d0a9b5f..f67d08c 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -225,6 +225,8 @@ namespace HyperTwistTrainingSubsystemInternal == TEXT("coach-memory-method-drill-follow-up-strong") || CoachMemorySnapshot.PreferredGuidanceSourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-strong") + || CoachMemorySnapshot.PreferredGuidanceSourceLabel + == TEXT("coach-memory-method-drill-follow-up-packet-structure-narrow") || CoachMemorySnapshot.PreferredCoachHandoffSourceLabel == TEXT("coach-memory-method-drill-follow-up-strong") || CoachMemorySnapshot.PreferredCoachHandoffSourceLabel.StartsWith( @@ -246,6 +248,8 @@ namespace HyperTwistTrainingSubsystemInternal == TEXT("coach-memory-method-drill-follow-up-weak") || CoachMemorySnapshot.PreferredGuidanceSourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-weak") + || CoachMemorySnapshot.PreferredGuidanceSourceLabel + == TEXT("coach-memory-method-drill-follow-up-packet-structure-broad") || CoachMemorySnapshot.PreferredCoachHandoffSourceLabel == TEXT("coach-memory-method-drill-follow-up-weak") || CoachMemorySnapshot.PreferredCoachHandoffSourceLabel.StartsWith( @@ -265,6 +269,8 @@ namespace HyperTwistTrainingSubsystemInternal { return CoachMemorySnapshot.PreferredGuidanceSourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-strong") + || CoachMemorySnapshot.PreferredGuidanceSourceLabel + == TEXT("coach-memory-method-drill-follow-up-packet-structure-narrow") || CoachMemorySnapshot.PreferredCoachHandoffSourceLabel.StartsWith( TEXT("coach-memory-dashboard-method-drill-follow-up-packet-strong-")); } @@ -274,6 +280,8 @@ namespace HyperTwistTrainingSubsystemInternal { return CoachMemorySnapshot.PreferredGuidanceSourceLabel == TEXT("coach-memory-method-drill-follow-up-packet-weak") + || CoachMemorySnapshot.PreferredGuidanceSourceLabel + == TEXT("coach-memory-method-drill-follow-up-packet-structure-broad") || CoachMemorySnapshot.PreferredCoachHandoffSourceLabel.StartsWith( TEXT("coach-memory-dashboard-method-drill-follow-up-packet-weak-")); } @@ -314,6 +322,42 @@ namespace HyperTwistTrainingSubsystemInternal } } + FString ResolveMethodDrillFollowUpPacketStructureToken( + const FHyperTwistTrainingCoachMemorySnapshot& CoachMemorySnapshot) + { + const bool bStrongMethodDrillPacketMemory = IsStrongMethodDrillFollowUpPacketMemory( + CoachMemorySnapshot + ); + const bool bWeakMethodDrillPacketMemory = IsWeakMethodDrillFollowUpPacketMemory( + CoachMemorySnapshot + ); + const EHyperTwistTrainingCoachHandoffKind PacketHandoffKind = + MethodDrillFollowUpPacketHandoffKindFromSourceLabel( + CoachMemorySnapshot.PreferredCoachHandoffSourceLabel, + CoachMemorySnapshot.PreferredCoachHandoffKind); + if (bStrongMethodDrillPacketMemory && !bWeakMethodDrillPacketMemory) + { + switch (PacketHandoffKind) + { + case EHyperTwistTrainingCoachHandoffKind::Queue: + return TEXT("packet_narrow_queue_entry_first"); + case EHyperTwistTrainingCoachHandoffKind::Recommended: + return TEXT("packet_narrow_recommended_brief_first"); + case EHyperTwistTrainingCoachHandoffKind::FollowUp: + default: + return TEXT("packet_narrow_follow_up_packet_first"); + } + } + if (bWeakMethodDrillPacketMemory && !bStrongMethodDrillPacketMemory) + { + return PacketHandoffKind == EHyperTwistTrainingCoachHandoffKind::Recommended + ? TEXT("packet_broad_recommended_brief_redirect") + : TEXT("packet_broad_broadened_redirect_first"); + } + + return FString(); + } + FHyperTwistTrainingSessionTemplate ApplyMethodDrillFollowUpMemoryBias( const FHyperTwistTrainingSessionTemplate& SessionTemplate, const FHyperTwistTrainingMethodDrillDiagnosticPacket& DiagnosticPacket, @@ -715,6 +759,14 @@ FHyperTwistTrainingCoachPanelState UHyperTwistTrainingSubsystem::GetActiveCoachP ActiveCoachMemorySnapshot.WeakMethodDrillFollowUpHistoryCount; PanelState.MethodDrillFollowUpAverageOutcomeScore = ActiveCoachMemorySnapshot.MethodDrillFollowUpAverageOutcomeScore; + PanelState.NarrowMethodDrillFollowUpPacketStructureHistoryCount = + ActiveCoachMemorySnapshot.NarrowMethodDrillFollowUpPacketStructureHistoryCount; + PanelState.BroadMethodDrillFollowUpPacketStructureHistoryCount = + ActiveCoachMemorySnapshot.BroadMethodDrillFollowUpPacketStructureHistoryCount; + PanelState.NarrowMethodDrillFollowUpPacketStructureAverageOutcomeScore = + ActiveCoachMemorySnapshot.NarrowMethodDrillFollowUpPacketStructureAverageOutcomeScore; + PanelState.BroadMethodDrillFollowUpPacketStructureAverageOutcomeScore = + ActiveCoachMemorySnapshot.BroadMethodDrillFollowUpPacketStructureAverageOutcomeScore; PanelState.MethodDrillRecoveryHistoryCount = ActiveCoachMemorySnapshot.MethodDrillRecoveryHistoryCount; PanelState.StrongMethodDrillRecoveryHistoryCount = @@ -1868,10 +1920,21 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartActiveMethodDrill const FString ResolvedSessionId = !SessionId.IsEmpty() ? SessionId : FString::Printf(TEXT("method_drill_follow_up_%s"), *ActiveMethodDrillRunState.RunId); + const FString MethodDrillPacketStructureToken = + SessionId.IsEmpty() + ? HyperTwistTrainingSubsystemInternal::ResolveMethodDrillFollowUpPacketStructureToken( + ActiveCoachMemorySnapshot) + : FString(); + const FString StructuredSessionId = !MethodDrillPacketStructureToken.IsEmpty() + ? FString::Printf( + TEXT("%s__%s"), + *ResolvedSessionId, + *MethodDrillPacketStructureToken) + : ResolvedSessionId; return StartTrainingRunFromDeck( FollowUpDeck, ActiveMethodDrillRunState.UserId, - ResolvedSessionId, + StructuredSessionId, ActiveMethodDrillFollowUpTemplate.SuggestedMode ); } diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index db54ff9..3cb8168 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -1935,6 +1935,18 @@ struct FHyperTwistTrainingCoachMemorySnapshot UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") float MethodDrillFollowUpAverageOutcomeScore = 0.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 NarrowMethodDrillFollowUpPacketStructureHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 BroadMethodDrillFollowUpPacketStructureHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float NarrowMethodDrillFollowUpPacketStructureAverageOutcomeScore = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float BroadMethodDrillFollowUpPacketStructureAverageOutcomeScore = 0.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") int32 MethodDrillRecoveryHistoryCount = 0; @@ -4912,6 +4924,18 @@ struct FHyperTwistTrainingCoachPanelState UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") float MethodDrillFollowUpAverageOutcomeScore = 0.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 NarrowMethodDrillFollowUpPacketStructureHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 BroadMethodDrillFollowUpPacketStructureHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float NarrowMethodDrillFollowUpPacketStructureAverageOutcomeScore = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float BroadMethodDrillFollowUpPacketStructureAverageOutcomeScore = 0.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") int32 MethodDrillRecoveryHistoryCount = 0;