diff --git a/docs/ops/UNTRACKED_SOURCE_PROTOTYPE_QUARANTINE.md b/docs/ops/UNTRACKED_SOURCE_PROTOTYPE_QUARANTINE.md new file mode 100644 index 0000000..0044de7 --- /dev/null +++ b/docs/ops/UNTRACKED_SOURCE_PROTOTYPE_QUARANTINE.md @@ -0,0 +1,64 @@ +# Untracked Source Prototype Quarantine + +Created on `2026-04-29` + +## Purpose + +This note records untracked prototype-style source files that were found under +`C:\HyperTwist\UnrealHyperTwist\Source\...` but were not part of the tracked +first-party runtime. + +The goal is to keep the product worktree clean without losing potentially +useful exploratory code. + +## Quarantined on 2026-04-29 + +Original untracked paths: + +- `C:\HyperTwist\UnrealHyperTwist\Source\HyperTwistCore\Public\DrillTypes.h` +- `C:\HyperTwist\UnrealHyperTwist\Source\HyperTwistTraining\Public\HyperTwistDrillManager.h` +- `C:\HyperTwist\UnrealHyperTwist\Source\HyperTwistTraining\Private\HyperTwistDrillManager.cpp` + +Quarantine destination: + +- `C:\HyperTwist\docs\quarantine\untracked-source-prototypes\2026-04-29\Source\HyperTwistCore\Public\DrillTypes.h` +- `C:\HyperTwist\docs\quarantine\untracked-source-prototypes\2026-04-29\Source\HyperTwistTraining\Public\HyperTwistDrillManager.h` +- `C:\HyperTwist\docs\quarantine\untracked-source-prototypes\2026-04-29\Source\HyperTwistTraining\Private\HyperTwistDrillManager.cpp` + +## Why these were quarantined instead of committed into runtime + +These files were not treated as live product code because: + +- they existed as untracked top-level source trees outside the tracked + `Source\UnrealHyperTwist` module surface +- no `Build.cs` files existed for `Source\HyperTwistCore` or + `Source\HyperTwistTraining` +- exact-name search found no references outside the prototype files themselves +- the contents describe a much earlier and narrower drill-manager concept than + the current first-party `HyperTwistTraining` stack + +Operationally, they are preserved as prototype artifacts, not accepted as +integrated runtime modules. + +## Reintegration rule + +Do not move these files back under `Source\...` unless all of the following are +true: + +1. there is a deliberate product task that needs them +2. they are reviewed against the current first-party training architecture +3. they are either reshaped into existing tracked modules or given a real + module/build definition +4. their runtime role is explicit and not duplicative of the current owned + `HyperTwistTraining` surfaces + +## Workflow guidance + +If future prototype code appears under top-level `Source\...` paths without a +real module definition or tracked task, prefer this same pattern: + +1. inspect whether it is integrated or stray +2. quarantine it into `docs\quarantine\untracked-source-prototypes\\...` +3. update this note with the original path and rationale + +This keeps the runtime tree clean while preserving handoff context. diff --git a/docs/quarantine/untracked-source-prototypes/2026-04-29/README.md b/docs/quarantine/untracked-source-prototypes/2026-04-29/README.md new file mode 100644 index 0000000..4896546 --- /dev/null +++ b/docs/quarantine/untracked-source-prototypes/2026-04-29/README.md @@ -0,0 +1,11 @@ +# Untracked source prototypes preserved on 2026-04-29 + +These files were moved out of the live `Source\...` tree because they were +untracked standalone prototypes, not integrated product modules. + +See: + +- `C:\HyperTwist\docs\ops\UNTRACKED_SOURCE_PROTOTYPE_QUARANTINE.md` + +The folder layout below preserves the original relative source paths so the +prototype can be reviewed later without polluting the runtime tree. diff --git a/docs/quarantine/untracked-source-prototypes/2026-04-29/Source/HyperTwistCore/Public/DrillTypes.h b/docs/quarantine/untracked-source-prototypes/2026-04-29/Source/HyperTwistCore/Public/DrillTypes.h new file mode 100644 index 0000000..3a7c8e6 --- /dev/null +++ b/docs/quarantine/untracked-source-prototypes/2026-04-29/Source/HyperTwistCore/Public/DrillTypes.h @@ -0,0 +1,51 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DataAsset.h" +#include "DrillTypes.generated.h" + +// Represents a single step or case within a drill. +USTRUCT(BlueprintType) +struct HYPERTWISTCORE_API FDrillCase +{ + GENERATED_BODY() + + // The initial state to present to the user (e.g., a scrambled cube state). + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Drill") + FString InitialState; + + // The target for a recognition drill (e.g., name of the algorithm). + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Drill") + FString RecognitionTarget; + + // The solution or next step. + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Drill") + FString Solution; +}; + +// A DataAsset that defines a collection of drill cases. +UCLASS(BlueprintType) +class HYPERTWISTCORE_API UHyperTwistDrillAsset : public UDataAsset +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Drill") + TArray DrillCases; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Drill") + FString MethodName; // e.g., "OLL", "PLL" +}; + +// Represents the user's saved progress and favorites. +USTRUCT(BlueprintType) +struct HYPERTWISTCORE_API FDrillUserData +{ + GENERATED_BODY() + + UPROPERTY() + TArray FavoriteCases; // Storing by InitialState as a key + + UPROPERTY() + TArray QueuedBatch; // A list of asset paths +}; diff --git a/docs/quarantine/untracked-source-prototypes/2026-04-29/Source/HyperTwistTraining/Private/HyperTwistDrillManager.cpp b/docs/quarantine/untracked-source-prototypes/2026-04-29/Source/HyperTwistTraining/Private/HyperTwistDrillManager.cpp new file mode 100644 index 0000000..7480278 --- /dev/null +++ b/docs/quarantine/untracked-source-prototypes/2026-04-29/Source/HyperTwistTraining/Private/HyperTwistDrillManager.cpp @@ -0,0 +1,109 @@ +#include "HyperTwistDrillManager.h" +#include "DrillTypes.h" +#include "Misc/Paths.h" +#include "Serialization/JsonSerializer.h" +#include "Serialization/JsonObjectConverter.h" +#include "HAL/PlatformFileManager.h" +#include "Misc/FileHelper.h" + +void UHyperTwistDrillManager::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + CurrentState = EDrillState::Idle; + CurrentCaseIndex = -1; + LoadUserData(); +} + +void UHyperTwistDrillManager::Deinitialize() +{ + SaveUserData(); + Super::Deinitialize(); +} + +FString GetSaveFilePath() +{ + return FPaths::ProjectSavedDir() + TEXT("DrillUserData.json"); +} + +void UHyperTwistDrillManager::LoadUserData() +{ + FString FilePath = GetSaveFilePath(); + FString JsonString; + + if (FFileHelper::LoadFileToString(JsonString, *FilePath)) + { + FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &UserData, 0, 0); + } +} + +void UHyperTwistDrillManager::SaveUserData() +{ + FString JsonString; + if (FJsonObjectConverter::UStructToJsonObjectString(UserData, JsonString)) + { + FFileHelper::SaveStringToFile(JsonString, *GetSaveFilePath()); + } +} + +void UHyperTwistDrillManager::SetState(EDrillState NewState) +{ + if (CurrentState != NewState) + { + CurrentState = NewState; + OnDrillStateChanged.Broadcast(CurrentState); + } +} + +void UHyperTwistDrillManager::StartDrill(UHyperTwistDrillAsset* DrillAsset) +{ + if (!DrillAsset || DrillAsset->DrillCases.Num() == 0) + { + return; + } + + ActiveDrillQueue = DrillAsset->DrillCases; + CurrentCaseIndex = 0; + + OnNewDrillCase.Broadcast(ActiveDrillQueue[CurrentCaseIndex]); + SetState(EDrillState::Presenting); +} + +void UHyperTwistDrillManager::Next() +{ + if (CurrentState == EDrillState::Idle || ActiveDrillQueue.Num() == 0) + { + return; + } + + CurrentCaseIndex++; + if (ActiveDrillQueue.IsValidIndex(CurrentCaseIndex)) + { + OnNewDrillCase.Broadcast(ActiveDrillQueue[CurrentCaseIndex]); + SetState(EDrillState::Presenting); + } + else + { + // Drill finished + CurrentCaseIndex = -1; + ActiveDrillQueue.Empty(); + SetState(EDrillState::Idle); + } +} + +void UHyperTwistDrillManager::ShowSolution() +{ + if (CurrentState == EDrillState::Presenting || CurrentState == EDrillState::AwaitingInput) + { + SetState(EDrillState::ShowingSolution); + } +} + +void UHyperTwistDrillManager::AddCurrentCaseToFavorites() +{ + if (ActiveDrillQueue.IsValidIndex(CurrentCaseIndex)) + { + const FString& caseId = ActiveDrillQueue[CurrentCaseIndex].InitialState; + UserData.FavoriteCases.AddUnique(caseId); + SaveUserData(); + } +} diff --git a/docs/quarantine/untracked-source-prototypes/2026-04-29/Source/HyperTwistTraining/Public/HyperTwistDrillManager.h b/docs/quarantine/untracked-source-prototypes/2026-04-29/Source/HyperTwistTraining/Public/HyperTwistDrillManager.h new file mode 100644 index 0000000..d4383df --- /dev/null +++ b/docs/quarantine/untracked-source-prototypes/2026-04-29/Source/HyperTwistTraining/Public/HyperTwistDrillManager.h @@ -0,0 +1,69 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "HyperTwistDrillManager.generated.h" + +// Forward declarations +struct FDrillCase; +class UHyperTwistDrillAsset; + +UENUM(BlueprintType) +enum class EDrillState : uint8 +{ + Idle, + Presenting, // Showing the initial state + AwaitingInput, // Waiting for user to recognize/solve + ShowingSolution +}; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDrillStateChanged, EDrillState, NewState); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnNewDrillCase, const FDrillCase&, NewCase); + +UCLASS() +class HYPERTWISTTRAINING_API UHyperTwistDrillManager : public UGameInstanceSubsystem +{ + GENERATED_BODY() + +public: + //~ Begin USubsystem + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; + //~ End USubsystem + + UFUNCTION(BlueprintCallable, Category = "Drill") + void StartDrill(UHyperTwistDrillAsset* DrillAsset); + + UFUNCTION(BlueprintCallable, Category = "Drill") + void Next(); + + UFUNCTION(BlueprintCallable, Category = "Drill") + void ShowSolution(); + + UFUNCTION(BlueprintCallable, Category = "Drill") + void AddCurrentCaseToFavorites(); + + UPROPERTY(BlueprintAssignable, Category = "Drill") + FOnDrillStateChanged OnDrillStateChanged; + + UPROPERTY(BlueprintAssignable, Category = "Drill") + FOnNewDrillCase OnNewDrillCase; + +private: + void LoadUserData(); + void SaveUserData(); + + void SetState(EDrillState NewState); + + UPROPERTY() + TArray ActiveDrillQueue; + + UPROPERTY() + int32 CurrentCaseIndex; + + UPROPERTY() + EDrillState CurrentState; + + UPROPERTY() + struct FDrillUserData UserData; +};