Overlay imported training catalog packs and finite bundles

This commit is contained in:
axiomlogicnexus 2026-05-01 22:00:33 +02:00
parent 27bf2469dc
commit 24736650ae

View file

@ -1297,11 +1297,19 @@ namespace HyperTwistTrainingCatalogMaterializationInternal
return false;
}
bool IsSupportedFiniteRepoBackedBundle(const FHyperTwistTrainingCatalogRuntimeContentBundle& RuntimeBundle)
bool IsSupportedFiniteImportedExerciseBundle(const FHyperTwistTrainingCatalogRuntimeContentBundle& RuntimeBundle)
{
const bool bIsSupportedFinitePolicy =
RuntimeBundle.RuntimeMaterializationPolicy.Equals(TEXT("instantiate-algorithm-cases"), ESearchCase::IgnoreCase)
|| RuntimeBundle.RuntimeMaterializationPolicy.Equals(TEXT("instantiate-scramble-drill-cases"), ESearchCase::IgnoreCase);
if (!bIsSupportedFinitePolicy)
{
return false;
}
return RuntimeBundle.ContentSourceFile.EndsWith(TEXT("repo-backed-exercises.jsonl"), ESearchCase::IgnoreCase)
&& (RuntimeBundle.RuntimeMaterializationPolicy.Equals(TEXT("instantiate-algorithm-cases"), ESearchCase::IgnoreCase)
|| RuntimeBundle.RuntimeMaterializationPolicy.Equals(TEXT("instantiate-scramble-drill-cases"), ESearchCase::IgnoreCase));
|| RuntimeBundle.ContentSourceFile.EndsWith(TEXT("httrack-retained-exercises.jsonl"), ESearchCase::IgnoreCase)
|| RuntimeBundle.ContentSourceFile.EndsWith(TEXT("restrictive-static-exercises.jsonl"), ESearchCase::IgnoreCase);
}
bool TryBuildTrainingCaseFromRawRecord(
@ -1392,10 +1400,11 @@ namespace HyperTwistTrainingCatalogMaterializationInternal
return OutCase.IsStructurallyValid();
}
bool TryMaterializeFiniteRepoBackedDeck(
bool TryMaterializeFiniteImportedDeck(
const FString& CatalogDirectory,
const FHyperTwistTrainingCatalogDeckImportSpec& DeckImportSpec,
const FHyperTwistTrainingCatalogRuntimeContentBundle& RuntimeBundle,
TMap<FString, TArray<FHyperTwistTrainingCatalogRawExerciseRecord>>& RawExerciseRecordCache,
FHyperTwistTrainingDeck& OutDeck,
TArray<FString>& InOutNotes
)
@ -1406,10 +1415,10 @@ namespace HyperTwistTrainingCatalogMaterializationInternal
InOutNotes.Add(TEXT("Imported deck materialization failed because the deck spec or runtime bundle was invalid."));
return false;
}
if (!IsSupportedFiniteRepoBackedBundle(RuntimeBundle))
if (!IsSupportedFiniteImportedExerciseBundle(RuntimeBundle))
{
InOutNotes.Add(FString::Printf(
TEXT("Imported deck %s remains deferred because runtime policy %s from %s is outside the current finite repo-backed slice."),
TEXT("Imported deck %s remains deferred because runtime policy %s from %s is outside the current finite imported-exercise slice."),
*DeckImportSpec.DeckId,
*RuntimeBundle.RuntimeMaterializationPolicy,
*RuntimeBundle.ContentSourceFile
@ -1459,19 +1468,25 @@ namespace HyperTwistTrainingCatalogMaterializationInternal
}
const FString SourcePath = ResolveBundledContentSourcePath(CatalogDirectory, RuntimeBundle.ContentSourceFile);
TArray<FHyperTwistTrainingCatalogRawExerciseRecord> RawRecords;
if (!LoadRawExerciseRecordsFromJsonLinesFile(
SourcePath,
RawRecords,
InOutNotes,
FString::Printf(TEXT("Imported raw exercise source for %s"), *DeckImportSpec.DeckId)))
TArray<FHyperTwistTrainingCatalogRawExerciseRecord>* RawRecords = RawExerciseRecordCache.Find(SourcePath);
if (RawRecords == nullptr)
{
return false;
TArray<FHyperTwistTrainingCatalogRawExerciseRecord> LoadedRawRecords;
if (!LoadRawExerciseRecordsFromJsonLinesFile(
SourcePath,
LoadedRawRecords,
InOutNotes,
FString::Printf(TEXT("Imported raw exercise source for %s"), *DeckImportSpec.DeckId)))
{
return false;
}
RawRecords = &RawExerciseRecordCache.Add(SourcePath, MoveTemp(LoadedRawRecords));
}
const FString PuzzleId = ResolveImportedPuzzleId(DeckImportSpec);
TArray<FHyperTwistTrainingCase> Cases;
for (const FHyperTwistTrainingCatalogRawExerciseRecord& RawRecord : RawRecords)
for (const FHyperTwistTrainingCatalogRawExerciseRecord& RawRecord : *RawRecords)
{
if (!MatchesRuntimeBundleFilter(RuntimeBundle, RawRecord))
{
@ -1543,17 +1558,200 @@ namespace HyperTwistTrainingCatalogMaterializationInternal
);
return OutDeck.IsStructurallyValid();
}
bool TryFindDeckImportSpecByImportSpecId(
const FHyperTwistTrainingCatalogMaterializedBundle& MaterializedBundle,
const FString& DeckImportSpecId,
FHyperTwistTrainingCatalogDeckImportSpec& OutDeckImportSpec
)
{
OutDeckImportSpec = FHyperTwistTrainingCatalogDeckImportSpec();
if (!MaterializedBundle.IsStructurallyValid() || DeckImportSpecId.IsEmpty())
{
return false;
}
for (const FHyperTwistTrainingCatalogDeckImportSpec& DeckImportSpec
: MaterializedBundle.DeckImportSpecs.deck_import_specs)
{
if (DeckImportSpec.DeckImportSpecId == DeckImportSpecId)
{
OutDeckImportSpec = DeckImportSpec;
return OutDeckImportSpec.IsStructurallyValid();
}
}
return false;
}
bool TryBuildImportedTrackFromManifest(
const FString& CatalogDirectory,
const FHyperTwistTrainingCatalogMaterializedBundle& MaterializedBundle,
const FHyperTwistTrainingCatalogImportedTrackManifest& TrackManifest,
TMap<FString, TArray<FHyperTwistTrainingCatalogRawExerciseRecord>>& RawExerciseRecordCache,
FHyperTwistTrainingTrack& OutTrack
)
{
OutTrack = FHyperTwistTrainingTrack();
if (!MaterializedBundle.IsStructurallyValid() || !TrackManifest.IsStructurallyValid())
{
return false;
}
FHyperTwistTrainingSourceAttribution TrackSourceAttribution;
if (!UHyperTwistTrainingCatalogLibrary::TryConvertImportedSourceAttribution(
TrackManifest.SourceAttribution,
TrackSourceAttribution))
{
return false;
}
TArray<FHyperTwistTrainingDeck> Decks;
for (const FString& DeckImportSpecId : TrackManifest.DeckImportSpecIds)
{
FHyperTwistTrainingCatalogDeckImportSpec DeckImportSpec;
if (!TryFindDeckImportSpecByImportSpecId(MaterializedBundle, DeckImportSpecId, DeckImportSpec))
{
continue;
}
FHyperTwistTrainingCatalogRuntimeContentBundle RuntimeBundle;
if (!UHyperTwistTrainingCatalogLibrary::TryFindRuntimeContentBundleForDeckImportSpec(
MaterializedBundle,
DeckImportSpec,
RuntimeBundle))
{
continue;
}
FHyperTwistTrainingDeck Deck;
TArray<FString> MaterializationNotes;
if (!TryMaterializeFiniteImportedDeck(
CatalogDirectory,
DeckImportSpec,
RuntimeBundle,
RawExerciseRecordCache,
Deck,
MaterializationNotes))
{
continue;
}
Decks.Add(MoveTemp(Deck));
}
if (Decks.Num() == 0)
{
return false;
}
OutTrack.TrackId = TrackManifest.TrackId;
OutTrack.Title = TrackManifest.Title;
OutTrack.Goal = TrackManifest.Goal;
OutTrack.Decks = MoveTemp(Decks);
OutTrack.SourceAttribution = TrackSourceAttribution;
return OutTrack.IsStructurallyValid();
}
bool TryBuildImportedContentPackFromManifest(
const FString& CatalogDirectory,
const FHyperTwistTrainingCatalogMaterializedBundle& MaterializedBundle,
const FHyperTwistTrainingCatalogImportedContentPackManifest& ContentPackManifest,
TMap<FString, TArray<FHyperTwistTrainingCatalogRawExerciseRecord>>& RawExerciseRecordCache,
FHyperTwistContentPack& OutContentPack
)
{
OutContentPack = FHyperTwistContentPack();
if (!MaterializedBundle.IsStructurallyValid() || !ContentPackManifest.IsStructurallyValid())
{
return false;
}
FHyperTwistTrainingSourceAttribution ContentPackSourceAttribution;
if (!UHyperTwistTrainingCatalogLibrary::TryConvertImportedSourceAttribution(
ContentPackManifest.SourceAttribution,
ContentPackSourceAttribution))
{
return false;
}
EHyperTwistPuzzleFamily PuzzleFamily = EHyperTwistPuzzleFamily::ClassicCube;
if (!UHyperTwistTrainingCatalogLibrary::TryConvertImportedPuzzleFamily(
ContentPackManifest.PuzzleFamily,
PuzzleFamily))
{
return false;
}
TArray<FHyperTwistTrainingTrack> Tracks;
for (const FHyperTwistTrainingCatalogImportedTrackManifest& TrackManifest : ContentPackManifest.TrackManifests)
{
FHyperTwistTrainingTrack Track;
if (TryBuildImportedTrackFromManifest(
CatalogDirectory,
MaterializedBundle,
TrackManifest,
RawExerciseRecordCache,
Track))
{
Tracks.Add(MoveTemp(Track));
}
}
if (Tracks.Num() == 0)
{
return false;
}
OutContentPack.ContentPackId = ContentPackManifest.ContentPackId;
OutContentPack.Title = ContentPackManifest.Title;
OutContentPack.Version = ContentPackManifest.Version.IsEmpty() ? OutContentPack.Version : ContentPackManifest.Version;
OutContentPack.PuzzleFamily = PuzzleFamily;
OutContentPack.NotationProfile =
ContentPackManifest.NotationProfile.IsEmpty() ? OutContentPack.NotationProfile : ContentPackManifest.NotationProfile;
OutContentPack.Tracks = MoveTemp(Tracks);
OutContentPack.SourceAttribution = ContentPackSourceAttribution;
return OutContentPack.IsStructurallyValid();
}
}
TArray<FHyperTwistContentPack> UHyperTwistTrainingCatalogLibrary::MakePhase3TrainingCatalog()
{
return
TArray<FHyperTwistContentPack> Catalog =
{
MakeAlgTrainerStarterContentPack(),
MakeCrossTrainerStarterContentPack(),
MakeFiveStyleStarterContentPack(),
UHyperTwistTrainingLibrary::MakeSampleClassicContentPack()
};
FHyperTwistTrainingCatalogMaterializedBundle MaterializedBundle;
FHyperTwistTrainingCatalogImportLoadReport LoadReport;
if (!LoadBundledCatalogMaterializationBundle(MaterializedBundle, LoadReport))
{
return Catalog;
}
TMap<FString, TArray<FHyperTwistTrainingCatalogRawExerciseRecord>> RawExerciseRecordCache;
const FString CatalogDirectory = GetBundledCatalogMaterializationDirectory();
for (const FHyperTwistTrainingCatalogImportedContentPackManifest& ContentPackManifest
: MaterializedBundle.ContentPackManifests.content_packs)
{
FHyperTwistContentPack ImportedContentPack;
if (!HyperTwistTrainingCatalogMaterializationInternal::TryBuildImportedContentPackFromManifest(
CatalogDirectory,
MaterializedBundle,
ContentPackManifest,
RawExerciseRecordCache,
ImportedContentPack))
{
continue;
}
Catalog.Add(MoveTemp(ImportedContentPack));
}
return Catalog;
}
FString UHyperTwistTrainingCatalogLibrary::GetBundledCatalogMaterializationDirectory()
@ -1772,10 +1970,12 @@ bool UHyperTwistTrainingCatalogLibrary::TryMaterializeImportedDeckFromMaterializ
return false;
}
const bool bMaterialized = HyperTwistTrainingCatalogMaterializationInternal::TryMaterializeFiniteRepoBackedDeck(
TMap<FString, TArray<FHyperTwistTrainingCatalogRawExerciseRecord>> RawExerciseRecordCache;
const bool bMaterialized = HyperTwistTrainingCatalogMaterializationInternal::TryMaterializeFiniteImportedDeck(
GetBundledCatalogMaterializationDirectory(),
DeckImportSpec,
RuntimeBundle,
RawExerciseRecordCache,
OutDeck,
OutLoadReport.Notes
);