diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 34b827e..0e0ccda 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -1052,6 +1052,24 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt() TEXT("CoachGuidanceComparisonDetail"), 8 ); + GuidanceOutcomeHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachGuidanceOutcomeHeader"), + 2 + ); + GuidanceOutcomeStatusTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachGuidanceOutcomeStatus"), + 2 + ); + GuidanceOutcomeDetailTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachGuidanceOutcomeDetail"), + 8 + ); CoachHandoffHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( WidgetTree, RootLayout, @@ -2615,6 +2633,95 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() DisplayedRunRecap->LastCaseId.IsEmpty() ? TEXT("n/a") : *DisplayedRunRecap->LastCaseId ) : TEXT("Comparison detail: no preview-launched recap is currently selected."); + const int32 DisplayedGuidanceComparisonNonSuccessCount = + DisplayedRunRecap != nullptr + ? (DisplayedRunRecap->FailureCount + DisplayedRunRecap->TimeoutCount + DisplayedRunRecap->DNFCount) + : 0; + FString GuidanceOutcomeStatusLine = + TEXT("Outcome: complete a run launched from previewed guidance to derive a repeat, broaden, or redirect verdict."); + FString GuidanceOutcomeDetailLine = + TEXT("Outcome detail: no preview-launched recap is currently selected."); + if (DisplayedRunRecap != nullptr + && DisplayedGuidanceComparisonBrief != nullptr + && DisplayedGuidanceComparisonDeck != nullptr + && DisplayedRunRecap->CompletedAttemptCount > 0) + { + if (bDisplayedGuidanceComparisonSingleCase) + { + if (DisplayedRunRecap->SuccessRate >= 0.85f + && DisplayedRunRecap->TimeoutCount == 0 + && DisplayedRunRecap->DNFCount == 0 + && DisplayedRunRecap->TotalMistakes <= 1) + { + GuidanceOutcomeStatusLine = + TEXT("Outcome: broaden guidance. The selected case looks stable enough to expand back to the wider preview deck."); + GuidanceOutcomeDetailLine = FString::Printf( + TEXT("Recommendation: move from the single-case launch into the broader %s preview deck or the next coach-directed handoff. Success rate %.2f across %d completed attempts with %d mistakes."), + *GuidanceComparisonLaneLabel, + DisplayedRunRecap->SuccessRate, + DisplayedRunRecap->CompletedAttemptCount, + DisplayedRunRecap->TotalMistakes + ); + } + else if (DisplayedRunRecap->SuccessRate >= 0.5f) + { + GuidanceOutcomeStatusLine = + TEXT("Outcome: stabilize, then broaden. The selected case is moving, but it is not yet clean enough for a wider shift."); + GuidanceOutcomeDetailLine = FString::Printf( + TEXT("Recommendation: repeat the selected preview case or a very small scope before broadening. Success rate %.2f with %d non-success outcomes and %d mistakes."), + DisplayedRunRecap->SuccessRate, + DisplayedGuidanceComparisonNonSuccessCount, + DisplayedRunRecap->TotalMistakes + ); + } + else + { + GuidanceOutcomeStatusLine = + TEXT("Outcome: repeat selected focus. The previewed case is still weak enough that broadening would be premature."); + GuidanceOutcomeDetailLine = FString::Printf( + TEXT("Recommendation: stay on the selected preview case until the recap is cleaner. Success rate %.2f with %d non-success outcomes and %d mistakes."), + DisplayedRunRecap->SuccessRate, + DisplayedGuidanceComparisonNonSuccessCount, + DisplayedRunRecap->TotalMistakes + ); + } + } + else if (DisplayedRunRecap->SuccessRate >= 0.8f + && DisplayedGuidanceComparisonNonSuccessCount <= FMath::Max(1, DisplayedRunRecap->CompletedAttemptCount / 4) + && DisplayedRunRecap->TotalMistakes <= FMath::Max(1, DisplayedRunRecap->CompletedAttemptCount / 2)) + { + GuidanceOutcomeStatusLine = + TEXT("Outcome: change guidance. The full preview deck looks stable enough to move on from this exact focus."); + GuidanceOutcomeDetailLine = FString::Printf( + TEXT("Recommendation: use follow-up, queued, or recommended coach handoff rather than repeating the same deck. Success rate %.2f across %d completed attempts with %d mistakes."), + DisplayedRunRecap->SuccessRate, + DisplayedRunRecap->CompletedAttemptCount, + DisplayedRunRecap->TotalMistakes + ); + } + else if (DisplayedRunRecap->SuccessRate >= 0.55f) + { + GuidanceOutcomeStatusLine = + TEXT("Outcome: repeat and stabilize. The deck is moving, but not cleanly enough to abandon this scope."); + GuidanceOutcomeDetailLine = FString::Printf( + TEXT("Recommendation: repeat the preview deck or stay near this focus before changing guidance. Success rate %.2f with %d non-success outcomes and %d mistakes."), + DisplayedRunRecap->SuccessRate, + DisplayedGuidanceComparisonNonSuccessCount, + DisplayedRunRecap->TotalMistakes + ); + } + else + { + GuidanceOutcomeStatusLine = + TEXT("Outcome: narrow or redirect guidance. The broad preview scope is still too unstable."); + GuidanceOutcomeDetailLine = FString::Printf( + TEXT("Recommendation: narrow back to a smaller focus case set or switch guidance lanes before repeating the full deck. Success rate %.2f with %d non-success outcomes and %d mistakes."), + DisplayedRunRecap->SuccessRate, + DisplayedGuidanceComparisonNonSuccessCount, + DisplayedRunRecap->TotalMistakes + ); + } + } const bool bCanStartPreviewDeck = !bHasLiveAttemptTimer && !bHasActiveRun && ActivePreviewDeck != nullptr && ActivePreviewDeck->IsStructurallyValid(); const bool bCanStartPreviewCase = bCanStartPreviewDeck && SelectedGuidancePreviewCase != nullptr; @@ -2743,6 +2850,18 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() { GuidanceComparisonDetailTextBlock->SetText(FText::FromString(GuidanceComparisonDetailLine)); } + if (GuidanceOutcomeHeaderTextBlock != nullptr) + { + GuidanceOutcomeHeaderTextBlock->SetText(FText::FromString(TEXT("[Guidance Outcome]"))); + } + if (GuidanceOutcomeStatusTextBlock != nullptr) + { + GuidanceOutcomeStatusTextBlock->SetText(FText::FromString(GuidanceOutcomeStatusLine)); + } + if (GuidanceOutcomeDetailTextBlock != nullptr) + { + GuidanceOutcomeDetailTextBlock->SetText(FText::FromString(GuidanceOutcomeDetailLine)); + } if (RepeatRunButtonLabel != nullptr) { RepeatRunButtonLabel->SetText(FText::FromString(TEXT("Repeat Run"))); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 28a4ab1..2d2433a 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -197,6 +197,15 @@ protected: UPROPERTY(Transient) TObjectPtr GuidanceComparisonDetailTextBlock = nullptr; + UPROPERTY(Transient) + TObjectPtr GuidanceOutcomeHeaderTextBlock = nullptr; + + UPROPERTY(Transient) + TObjectPtr GuidanceOutcomeStatusTextBlock = nullptr; + + UPROPERTY(Transient) + TObjectPtr GuidanceOutcomeDetailTextBlock = nullptr; + UPROPERTY(Transient) TObjectPtr CoachHandoffHeaderTextBlock = nullptr;