Implement Phase 6R-C reconstruction session contract
This commit is contained in:
parent
47670a764a
commit
175fdce3fb
10 changed files with 1616 additions and 37 deletions
|
|
@ -131,6 +131,55 @@ namespace HyperTwistContractLibraryInternal
|
|||
return Profile;
|
||||
}
|
||||
|
||||
void ResolveMockClassicFaceSwatch(
|
||||
const FString& FaceId,
|
||||
FString& OutColorId,
|
||||
FString& OutNotation,
|
||||
FIntVector& OutSampleBgr
|
||||
)
|
||||
{
|
||||
const FString NormalizedFaceId = FaceId.ToUpper();
|
||||
if (NormalizedFaceId == TEXT("U"))
|
||||
{
|
||||
OutColorId = TEXT("white");
|
||||
OutNotation = TEXT("U");
|
||||
OutSampleBgr = FIntVector(255, 255, 255);
|
||||
return;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("R"))
|
||||
{
|
||||
OutColorId = TEXT("red");
|
||||
OutNotation = TEXT("R");
|
||||
OutSampleBgr = FIntVector(0, 0, 255);
|
||||
return;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("D"))
|
||||
{
|
||||
OutColorId = TEXT("yellow");
|
||||
OutNotation = TEXT("D");
|
||||
OutSampleBgr = FIntVector(0, 255, 255);
|
||||
return;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("L"))
|
||||
{
|
||||
OutColorId = TEXT("orange");
|
||||
OutNotation = TEXT("L");
|
||||
OutSampleBgr = FIntVector(0, 165, 255);
|
||||
return;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("B"))
|
||||
{
|
||||
OutColorId = TEXT("blue");
|
||||
OutNotation = TEXT("B");
|
||||
OutSampleBgr = FIntVector(255, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
OutColorId = TEXT("green");
|
||||
OutNotation = TEXT("F");
|
||||
OutSampleBgr = FIntVector(0, 255, 0);
|
||||
}
|
||||
|
||||
FHyperTwistVisionFaceObservation MakeMockClassicFaceObservation(
|
||||
const FString& ObservationId,
|
||||
const FString& FaceId,
|
||||
|
|
@ -148,21 +197,103 @@ namespace HyperTwistContractLibraryInternal
|
|||
Observation.bOrderingStable = true;
|
||||
Observation.bCommitReady = bCommitReady;
|
||||
|
||||
FString ColorId;
|
||||
FString Notation;
|
||||
FIntVector SampleBgr = FIntVector::ZeroValue;
|
||||
ResolveMockClassicFaceSwatch(FaceId, ColorId, Notation, SampleBgr);
|
||||
|
||||
for (int32 GridIndex = 0; GridIndex < 9; ++GridIndex)
|
||||
{
|
||||
FHyperTwistVisionObservedSticker Sticker;
|
||||
Sticker.GridIndex = GridIndex;
|
||||
Sticker.GridRow = GridIndex / 3;
|
||||
Sticker.GridColumn = GridIndex % 3;
|
||||
Sticker.ColorId = TEXT("green");
|
||||
Sticker.Notation = TEXT("F");
|
||||
Sticker.SampleBgr = FIntVector(0, 255, 0);
|
||||
Sticker.ColorId = ColorId;
|
||||
Sticker.Notation = Notation;
|
||||
Sticker.SampleBgr = SampleBgr;
|
||||
Sticker.Confidence = StickerConfidence;
|
||||
Observation.OrderedStickers.Add(Sticker);
|
||||
}
|
||||
|
||||
return Observation;
|
||||
}
|
||||
|
||||
FHyperTwistVisionReconstructionSession MakeMockClassicReconstructionSession(
|
||||
const FString& SessionId,
|
||||
const TArray<FString>& FaceIds,
|
||||
const float StickerConfidence
|
||||
)
|
||||
{
|
||||
FHyperTwistVisionReconstructionSession Session;
|
||||
Session.SessionId = SessionId;
|
||||
Session.PuzzleId = TEXT("cube/3x3x3");
|
||||
Session.ReconstructionProfile = TEXT("classic-face-vote-v1");
|
||||
Session.OrientationProfile = TEXT("classic-target-face-net-v1");
|
||||
Session.bUsesObservationVoting = true;
|
||||
Session.bHasStableOrientation = FaceIds.Num() > 0;
|
||||
|
||||
const TArray<FString> ExpectedFaces = {
|
||||
TEXT("U"),
|
||||
TEXT("R"),
|
||||
TEXT("F"),
|
||||
TEXT("D"),
|
||||
TEXT("L"),
|
||||
TEXT("B")
|
||||
};
|
||||
|
||||
for (const FString& FaceId : FaceIds)
|
||||
{
|
||||
FHyperTwistVisionFaceObservation Observation = MakeMockClassicFaceObservation(
|
||||
FString::Printf(TEXT("%s/%s"), *SessionId, *FaceId),
|
||||
FaceId,
|
||||
StickerConfidence,
|
||||
true
|
||||
);
|
||||
|
||||
FHyperTwistVisionCommittedFaceState FaceState;
|
||||
FaceState.FaceId = FaceId;
|
||||
FaceState.LatestObservationId = Observation.ObservationId;
|
||||
FaceState.OrderingProfile = Observation.OrderingProfile;
|
||||
FaceState.ObservationCount = 1;
|
||||
FaceState.RevisionCount = 0;
|
||||
FaceState.LatestOrderedStickers = Observation.OrderedStickers;
|
||||
|
||||
for (const FHyperTwistVisionObservedSticker& Sticker : Observation.OrderedStickers)
|
||||
{
|
||||
FHyperTwistVisionStickerVoteTally Tally;
|
||||
Tally.Notation = Sticker.Notation;
|
||||
Tally.ColorId = Sticker.ColorId;
|
||||
Tally.VoteCount = 1;
|
||||
|
||||
FHyperTwistVisionReconstructionStickerVote Vote;
|
||||
Vote.GridIndex = Sticker.GridIndex;
|
||||
Vote.GridRow = Sticker.GridRow;
|
||||
Vote.GridColumn = Sticker.GridColumn;
|
||||
Vote.WinningNotation = Sticker.Notation;
|
||||
Vote.WinningColorId = Sticker.ColorId;
|
||||
Vote.WinningVoteCount = 1;
|
||||
Vote.TotalVoteCount = 1;
|
||||
Vote.Tallies.Add(Tally);
|
||||
FaceState.StickerVotes.Add(Vote);
|
||||
}
|
||||
|
||||
Session.CommittedFaces.Add(FaceState);
|
||||
}
|
||||
|
||||
Session.TotalCommittedFaceCount = Session.CommittedFaces.Num();
|
||||
Session.TotalRevisionCount = 0;
|
||||
Session.ConfidenceRollup = StickerConfidence;
|
||||
|
||||
for (const FString& ExpectedFace : ExpectedFaces)
|
||||
{
|
||||
if (!FaceIds.Contains(ExpectedFace))
|
||||
{
|
||||
Session.MissingFaces.Add(ExpectedFace);
|
||||
}
|
||||
}
|
||||
Session.bHasCompleteClassicCubeNet = Session.MissingFaces.Num() == 0;
|
||||
return Session;
|
||||
}
|
||||
}
|
||||
|
||||
FHyperTwistPuzzleDefinitionRef UHyperTwistContractLibrary::MakeSampleClassicPuzzleDefinition()
|
||||
|
|
@ -435,6 +566,11 @@ FHyperTwistVisionCommitResult UHyperTwistContractLibrary::MakeMockVisionCommitRe
|
|||
0.96f,
|
||||
true
|
||||
);
|
||||
Result.ReconstructionSession = HyperTwistContractLibraryInternal::MakeMockClassicReconstructionSession(
|
||||
Result.SessionId,
|
||||
{TEXT("F")},
|
||||
Result.Confidence
|
||||
);
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
|
@ -449,8 +585,13 @@ FHyperTwistVisionFinalizeResult UHyperTwistContractLibrary::MakeMockVisionFinali
|
|||
Result.FinalSnapshot.SnapshotId = TEXT("snap_final");
|
||||
Result.FinalSnapshot.State = Result.FinalState;
|
||||
Result.FinalSnapshot.DerivedHash = TEXT("final_hash");
|
||||
Result.ReplaySeedEventsJson.Add(SerializeReplayPacketToJson(MakeSampleRecognitionReplayPacket()));
|
||||
Result.ConfidenceRollup = 0.97f;
|
||||
Result.ReconstructionSession = HyperTwistContractLibraryInternal::MakeMockClassicReconstructionSession(
|
||||
Result.SessionId,
|
||||
{TEXT("U"), TEXT("R"), TEXT("F"), TEXT("D"), TEXT("L"), TEXT("B")},
|
||||
Result.ConfidenceRollup
|
||||
);
|
||||
Result.ReplaySeedEventsJson.Add(SerializeReplayPacketToJson(MakeSampleRecognitionReplayPacket()));
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
|
@ -464,7 +605,8 @@ FHyperTwistVisionServiceHealth UHyperTwistContractLibrary::MakeMockVisionService
|
|||
TEXT("liveCamera3x3"),
|
||||
TEXT("faceCommitFlow"),
|
||||
TEXT("calibrationProfiles"),
|
||||
TEXT("orderedFaceObservation")
|
||||
TEXT("orderedFaceObservation"),
|
||||
TEXT("reconstructionSession")
|
||||
};
|
||||
Health.bReady = true;
|
||||
return Health;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,78 @@
|
|||
|
||||
#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"
|
||||
|
||||
namespace HyperTwistMockVisionClientInternal
|
||||
{
|
||||
void ResolveMockClassicFaceSwatch(
|
||||
const FString& FaceId,
|
||||
FString& OutColorId,
|
||||
FString& OutNotation,
|
||||
FIntVector& OutSampleBgr
|
||||
)
|
||||
{
|
||||
const FString NormalizedFaceId = FaceId.ToUpper();
|
||||
if (NormalizedFaceId == TEXT("U"))
|
||||
{
|
||||
OutColorId = TEXT("white");
|
||||
OutNotation = TEXT("U");
|
||||
OutSampleBgr = FIntVector(255, 255, 255);
|
||||
return;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("R"))
|
||||
{
|
||||
OutColorId = TEXT("red");
|
||||
OutNotation = TEXT("R");
|
||||
OutSampleBgr = FIntVector(0, 0, 255);
|
||||
return;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("D"))
|
||||
{
|
||||
OutColorId = TEXT("yellow");
|
||||
OutNotation = TEXT("D");
|
||||
OutSampleBgr = FIntVector(0, 255, 255);
|
||||
return;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("L"))
|
||||
{
|
||||
OutColorId = TEXT("orange");
|
||||
OutNotation = TEXT("L");
|
||||
OutSampleBgr = FIntVector(0, 165, 255);
|
||||
return;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("B"))
|
||||
{
|
||||
OutColorId = TEXT("blue");
|
||||
OutNotation = TEXT("B");
|
||||
OutSampleBgr = FIntVector(255, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
OutColorId = TEXT("green");
|
||||
OutNotation = TEXT("F");
|
||||
OutSampleBgr = FIntVector(0, 255, 0);
|
||||
}
|
||||
|
||||
void ApplyMockClassicFaceToObservation(
|
||||
FHyperTwistVisionFaceObservation& Observation,
|
||||
const FString& FaceId
|
||||
)
|
||||
{
|
||||
Observation.FaceId = FaceId;
|
||||
|
||||
FString ColorId;
|
||||
FString Notation;
|
||||
FIntVector SampleBgr = FIntVector::ZeroValue;
|
||||
ResolveMockClassicFaceSwatch(FaceId, ColorId, Notation, SampleBgr);
|
||||
|
||||
for (FHyperTwistVisionObservedSticker& Sticker : Observation.OrderedStickers)
|
||||
{
|
||||
Sticker.ColorId = ColorId;
|
||||
Sticker.Notation = Notation;
|
||||
Sticker.SampleBgr = SampleBgr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UHyperTwistMockVisionClient::OpenVisionSession(const FHyperTwistVisionSessionConfig& Config, FString& OutError)
|
||||
{
|
||||
OutError.Reset();
|
||||
|
|
@ -26,7 +98,10 @@ FHyperTwistVisionPreviewResult UHyperTwistMockVisionClient::SubmitVisionFrame(co
|
|||
|
||||
const int32 NextFrameCount = SessionFrameCounts.FindRef(Frame.SessionId) + 1;
|
||||
SessionFrameCounts.Add(Frame.SessionId, NextFrameCount);
|
||||
Result.ObservedFace.FaceId = !Frame.TargetFaceHint.IsEmpty() ? Frame.TargetFaceHint : Result.ObservedFace.FaceId;
|
||||
HyperTwistMockVisionClientInternal::ApplyMockClassicFaceToObservation(
|
||||
Result.ObservedFace,
|
||||
!Frame.TargetFaceHint.IsEmpty() ? Frame.TargetFaceHint : Result.ObservedFace.FaceId
|
||||
);
|
||||
Result.ObservedFace.ObservationId = Result.FrameId.IsEmpty()
|
||||
? TEXT("observation_preview")
|
||||
: FString::Printf(TEXT("%s/preview"), *Result.FrameId);
|
||||
|
|
@ -43,7 +118,10 @@ FHyperTwistVisionCommitResult UHyperTwistMockVisionClient::CommitVisionObservati
|
|||
Result.CommitKind = Request.CommitKind;
|
||||
Result.CommittedUnit = !Request.TargetFace.IsEmpty() ? Request.TargetFace : Request.TargetStage;
|
||||
Result.Snapshot.State.SourceSessionId = Request.SessionId;
|
||||
Result.ObservedFace.FaceId = !Request.TargetFace.IsEmpty() ? Request.TargetFace : Result.ObservedFace.FaceId;
|
||||
HyperTwistMockVisionClientInternal::ApplyMockClassicFaceToObservation(
|
||||
Result.ObservedFace,
|
||||
!Request.TargetFace.IsEmpty() ? Request.TargetFace : Result.ObservedFace.FaceId
|
||||
);
|
||||
Result.ObservedFace.ObservationId = Request.SessionId.IsEmpty()
|
||||
? TEXT("observation_commit")
|
||||
: FString::Printf(TEXT("%s/commit"), *Request.SessionId);
|
||||
|
|
|
|||
|
|
@ -1872,6 +1872,464 @@ namespace HyperTwistTrainingSubsystemInternal
|
|||
&& SessionConfig.PuzzleId == TEXT("cube/3x3x3");
|
||||
}
|
||||
|
||||
const TArray<FString>& GetClassicRecognitionFaceOrder()
|
||||
{
|
||||
static const TArray<FString> FaceOrder = {
|
||||
TEXT("U"),
|
||||
TEXT("R"),
|
||||
TEXT("F"),
|
||||
TEXT("D"),
|
||||
TEXT("L"),
|
||||
TEXT("B")
|
||||
};
|
||||
return FaceOrder;
|
||||
}
|
||||
|
||||
FString NormalizeRecognitionFaceId(const FString& FaceId)
|
||||
{
|
||||
FString Normalized = FaceId;
|
||||
Normalized = Normalized.TrimStartAndEnd();
|
||||
Normalized.ToUpperInline();
|
||||
return Normalized;
|
||||
}
|
||||
|
||||
int32 GetClassicRecognitionFaceOrderIndex(const FString& FaceId)
|
||||
{
|
||||
const FString NormalizedFaceId = NormalizeRecognitionFaceId(FaceId);
|
||||
const TArray<FString>& FaceOrder = GetClassicRecognitionFaceOrder();
|
||||
for (int32 Index = 0; Index < FaceOrder.Num(); ++Index)
|
||||
{
|
||||
if (FaceOrder[Index] == NormalizedFaceId)
|
||||
{
|
||||
return Index;
|
||||
}
|
||||
}
|
||||
|
||||
return MAX_int32;
|
||||
}
|
||||
|
||||
const FHyperTwistVisionCommittedFaceState* FindCommittedFaceState(
|
||||
const FHyperTwistVisionReconstructionSession& Session,
|
||||
const FString& FaceId
|
||||
)
|
||||
{
|
||||
const FString NormalizedFaceId = NormalizeRecognitionFaceId(FaceId);
|
||||
return Session.CommittedFaces.FindByPredicate(
|
||||
[&NormalizedFaceId](const FHyperTwistVisionCommittedFaceState& Candidate)
|
||||
{
|
||||
return NormalizeRecognitionFaceId(Candidate.FaceId) == NormalizedFaceId;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const FHyperTwistVisionReconstructionStickerVote* FindStickerVote(
|
||||
const FHyperTwistVisionCommittedFaceState& FaceState,
|
||||
const int32 GridIndex
|
||||
)
|
||||
{
|
||||
return FaceState.StickerVotes.FindByPredicate(
|
||||
[GridIndex](const FHyperTwistVisionReconstructionStickerVote& Candidate)
|
||||
{
|
||||
return Candidate.GridIndex == GridIndex;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
FString MakeStickerVoteKey(const FString& Notation, const FString& ColorId)
|
||||
{
|
||||
FString NormalizedColorId = ColorId;
|
||||
NormalizedColorId.ToLowerInline();
|
||||
return FString::Printf(
|
||||
TEXT("%s|%s"),
|
||||
*NormalizeRecognitionFaceId(Notation),
|
||||
*NormalizedColorId
|
||||
);
|
||||
}
|
||||
|
||||
FString ResolveClassicFaceletToken(const FString& Notation, const FString& ColorId)
|
||||
{
|
||||
FString NormalizedColorId = ColorId;
|
||||
NormalizedColorId.ToLowerInline();
|
||||
if (NormalizedColorId == TEXT("white"))
|
||||
{
|
||||
return TEXT("W");
|
||||
}
|
||||
if (NormalizedColorId == TEXT("red"))
|
||||
{
|
||||
return TEXT("R");
|
||||
}
|
||||
if (NormalizedColorId == TEXT("green"))
|
||||
{
|
||||
return TEXT("G");
|
||||
}
|
||||
if (NormalizedColorId == TEXT("yellow"))
|
||||
{
|
||||
return TEXT("Y");
|
||||
}
|
||||
if (NormalizedColorId == TEXT("orange"))
|
||||
{
|
||||
return TEXT("O");
|
||||
}
|
||||
if (NormalizedColorId == TEXT("blue"))
|
||||
{
|
||||
return TEXT("B");
|
||||
}
|
||||
|
||||
const FString NormalizedNotation = NormalizeRecognitionFaceId(Notation);
|
||||
if (NormalizedNotation == TEXT("U"))
|
||||
{
|
||||
return TEXT("W");
|
||||
}
|
||||
if (NormalizedNotation == TEXT("R"))
|
||||
{
|
||||
return TEXT("R");
|
||||
}
|
||||
if (NormalizedNotation == TEXT("F"))
|
||||
{
|
||||
return TEXT("G");
|
||||
}
|
||||
if (NormalizedNotation == TEXT("D"))
|
||||
{
|
||||
return TEXT("Y");
|
||||
}
|
||||
if (NormalizedNotation == TEXT("L"))
|
||||
{
|
||||
return TEXT("O");
|
||||
}
|
||||
if (NormalizedNotation == TEXT("B"))
|
||||
{
|
||||
return TEXT("B");
|
||||
}
|
||||
|
||||
return FString();
|
||||
}
|
||||
|
||||
bool IsUniformFaceletTokenArray(const TArray<FString>& FaceTokens)
|
||||
{
|
||||
if (FaceTokens.Num() != 9 || FaceTokens[0].IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32 Index = 1; Index < FaceTokens.Num(); ++Index)
|
||||
{
|
||||
if (FaceTokens[Index] != FaceTokens[0])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TArray<FString>* ResolveMutableClassicFaceTokens(
|
||||
FHyperTwistClassicFaceletSnapshotState& FaceletState,
|
||||
const FString& FaceId
|
||||
)
|
||||
{
|
||||
const FString NormalizedFaceId = NormalizeRecognitionFaceId(FaceId);
|
||||
if (NormalizedFaceId == TEXT("U"))
|
||||
{
|
||||
return &FaceletState.U;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("R"))
|
||||
{
|
||||
return &FaceletState.R;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("F"))
|
||||
{
|
||||
return &FaceletState.F;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("D"))
|
||||
{
|
||||
return &FaceletState.D;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("L"))
|
||||
{
|
||||
return &FaceletState.L;
|
||||
}
|
||||
if (NormalizedFaceId == TEXT("B"))
|
||||
{
|
||||
return &FaceletState.B;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FHyperTwistVisionReconstructionSession BuildClassicRecognitionReconstructionSession(
|
||||
const FHyperTwistVisionSessionConfig& SessionConfig,
|
||||
const FString& SessionId,
|
||||
const FHyperTwistVisionReconstructionSession& ExistingSession,
|
||||
const FHyperTwistVisionFaceObservation& Observation,
|
||||
bool& OutWasRevision
|
||||
)
|
||||
{
|
||||
OutWasRevision = false;
|
||||
|
||||
FHyperTwistVisionReconstructionSession Session = ExistingSession.IsStructurallyValid()
|
||||
? ExistingSession
|
||||
: FHyperTwistVisionReconstructionSession();
|
||||
Session.SessionId = SessionId;
|
||||
Session.PuzzleId = !SessionConfig.PuzzleId.IsEmpty() ? SessionConfig.PuzzleId : TEXT("cube/3x3x3");
|
||||
Session.ReconstructionProfile = TEXT("classic-face-vote-v1");
|
||||
Session.OrientationProfile = TEXT("classic-target-face-net-v1");
|
||||
Session.bUsesObservationVoting = true;
|
||||
|
||||
const FString FaceId = NormalizeRecognitionFaceId(Observation.FaceId);
|
||||
const FHyperTwistVisionCommittedFaceState* PreviousFaceState = FindCommittedFaceState(Session, FaceId);
|
||||
OutWasRevision = PreviousFaceState != nullptr;
|
||||
|
||||
FHyperTwistVisionCommittedFaceState FaceState;
|
||||
FaceState.FaceId = FaceId;
|
||||
FaceState.LatestObservationId = Observation.ObservationId;
|
||||
FaceState.OrderingProfile = Observation.OrderingProfile;
|
||||
FaceState.ObservationCount = PreviousFaceState != nullptr
|
||||
? PreviousFaceState->ObservationCount + 1
|
||||
: 1;
|
||||
FaceState.RevisionCount = PreviousFaceState != nullptr
|
||||
? PreviousFaceState->RevisionCount + 1
|
||||
: 0;
|
||||
FaceState.LatestOrderedStickers = Observation.OrderedStickers;
|
||||
|
||||
for (const FHyperTwistVisionObservedSticker& Sticker : Observation.OrderedStickers)
|
||||
{
|
||||
TArray<FHyperTwistVisionStickerVoteTally> Tallies;
|
||||
if (PreviousFaceState != nullptr)
|
||||
{
|
||||
if (const FHyperTwistVisionReconstructionStickerVote* PreviousVote =
|
||||
FindStickerVote(*PreviousFaceState, Sticker.GridIndex))
|
||||
{
|
||||
Tallies = PreviousVote->Tallies;
|
||||
}
|
||||
}
|
||||
|
||||
const FString StickerVoteKey = MakeStickerVoteKey(Sticker.Notation, Sticker.ColorId);
|
||||
bool bUpdatedExistingTally = false;
|
||||
for (FHyperTwistVisionStickerVoteTally& Tally : Tallies)
|
||||
{
|
||||
if (MakeStickerVoteKey(Tally.Notation, Tally.ColorId) == StickerVoteKey)
|
||||
{
|
||||
++Tally.VoteCount;
|
||||
bUpdatedExistingTally = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bUpdatedExistingTally)
|
||||
{
|
||||
FHyperTwistVisionStickerVoteTally Tally;
|
||||
Tally.Notation = NormalizeRecognitionFaceId(Sticker.Notation);
|
||||
Tally.ColorId = Sticker.ColorId;
|
||||
Tally.VoteCount = 1;
|
||||
Tallies.Add(Tally);
|
||||
}
|
||||
|
||||
int32 WinningIndex = INDEX_NONE;
|
||||
int32 TotalVoteCount = 0;
|
||||
for (int32 TallyIndex = 0; TallyIndex < Tallies.Num(); ++TallyIndex)
|
||||
{
|
||||
const FHyperTwistVisionStickerVoteTally& Tally = Tallies[TallyIndex];
|
||||
TotalVoteCount += Tally.VoteCount;
|
||||
|
||||
const bool bPreferThisTally = WinningIndex == INDEX_NONE
|
||||
|| Tally.VoteCount > Tallies[WinningIndex].VoteCount
|
||||
|| (Tally.VoteCount == Tallies[WinningIndex].VoteCount
|
||||
&& MakeStickerVoteKey(Tally.Notation, Tally.ColorId) == StickerVoteKey);
|
||||
if (bPreferThisTally)
|
||||
{
|
||||
WinningIndex = TallyIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (WinningIndex == INDEX_NONE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const FHyperTwistVisionStickerVoteTally& WinningTally = Tallies[WinningIndex];
|
||||
|
||||
FHyperTwistVisionReconstructionStickerVote Vote;
|
||||
Vote.GridIndex = Sticker.GridIndex;
|
||||
Vote.GridRow = Sticker.GridRow;
|
||||
Vote.GridColumn = Sticker.GridColumn;
|
||||
Vote.WinningNotation = WinningTally.Notation;
|
||||
Vote.WinningColorId = WinningTally.ColorId;
|
||||
Vote.WinningVoteCount = WinningTally.VoteCount;
|
||||
Vote.TotalVoteCount = TotalVoteCount;
|
||||
Vote.bHadConflict = Tallies.Num() > 1;
|
||||
Vote.Tallies = Tallies;
|
||||
FaceState.bHasConflicts = FaceState.bHasConflicts || Vote.bHadConflict;
|
||||
FaceState.StickerVotes.Add(Vote);
|
||||
}
|
||||
|
||||
const int32 ExistingFaceIndex = Session.CommittedFaces.IndexOfByPredicate(
|
||||
[&FaceId](const FHyperTwistVisionCommittedFaceState& Candidate)
|
||||
{
|
||||
return NormalizeRecognitionFaceId(Candidate.FaceId) == FaceId;
|
||||
}
|
||||
);
|
||||
if (ExistingFaceIndex == INDEX_NONE)
|
||||
{
|
||||
Session.CommittedFaces.Add(FaceState);
|
||||
}
|
||||
else
|
||||
{
|
||||
Session.CommittedFaces[ExistingFaceIndex] = FaceState;
|
||||
}
|
||||
|
||||
Session.CommittedFaces.Sort(
|
||||
[](const FHyperTwistVisionCommittedFaceState& Left, const FHyperTwistVisionCommittedFaceState& Right)
|
||||
{
|
||||
return GetClassicRecognitionFaceOrderIndex(Left.FaceId)
|
||||
< GetClassicRecognitionFaceOrderIndex(Right.FaceId);
|
||||
}
|
||||
);
|
||||
|
||||
Session.TotalCommittedFaceCount = Session.CommittedFaces.Num();
|
||||
Session.TotalRevisionCount = 0;
|
||||
float ConfidenceSum = 0.0f;
|
||||
int32 ConfidenceCount = 0;
|
||||
for (const FHyperTwistVisionCommittedFaceState& CommittedFace : Session.CommittedFaces)
|
||||
{
|
||||
Session.TotalRevisionCount += CommittedFace.RevisionCount;
|
||||
for (const FHyperTwistVisionObservedSticker& LatestSticker : CommittedFace.LatestOrderedStickers)
|
||||
{
|
||||
ConfidenceSum += LatestSticker.Confidence;
|
||||
++ConfidenceCount;
|
||||
}
|
||||
}
|
||||
Session.ConfidenceRollup = ConfidenceCount > 0
|
||||
? ConfidenceSum / static_cast<float>(ConfidenceCount)
|
||||
: 0.0f;
|
||||
Session.bHasStableOrientation = Session.TotalCommittedFaceCount > 0;
|
||||
Session.OrientationStateOrdinal = Session.TotalCommittedFaceCount;
|
||||
|
||||
Session.MissingFaces.Reset();
|
||||
for (const FString& ExpectedFaceId : GetClassicRecognitionFaceOrder())
|
||||
{
|
||||
if (FindCommittedFaceState(Session, ExpectedFaceId) == nullptr)
|
||||
{
|
||||
Session.MissingFaces.Add(ExpectedFaceId);
|
||||
}
|
||||
}
|
||||
Session.bHasCompleteClassicCubeNet =
|
||||
Session.MissingFaces.Num() == 0
|
||||
&& Session.CommittedFaces.Num() == GetClassicRecognitionFaceOrder().Num();
|
||||
|
||||
return Session;
|
||||
}
|
||||
|
||||
bool TryBuildClassicFaceletSnapshotFromReconstruction(
|
||||
const FHyperTwistVisionReconstructionSession& Session,
|
||||
FHyperTwistClassicFaceletSnapshotState& OutFaceletState
|
||||
)
|
||||
{
|
||||
if (!Session.IsStructurallyValid() || !Session.bHasCompleteClassicCubeNet)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OutFaceletState = FHyperTwistClassicFaceletSnapshotState();
|
||||
OutFaceletState.bPreviewState = false;
|
||||
|
||||
for (const FString& ExpectedFaceId : GetClassicRecognitionFaceOrder())
|
||||
{
|
||||
const FHyperTwistVisionCommittedFaceState* CommittedFace = FindCommittedFaceState(Session, ExpectedFaceId);
|
||||
TArray<FString>* FaceTokens = ResolveMutableClassicFaceTokens(OutFaceletState, ExpectedFaceId);
|
||||
if (CommittedFace == nullptr || FaceTokens == nullptr || CommittedFace->StickerVotes.Num() != 9)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FaceTokens->Reset();
|
||||
for (int32 GridIndex = 0; GridIndex < 9; ++GridIndex)
|
||||
{
|
||||
const FHyperTwistVisionReconstructionStickerVote* Vote = FindStickerVote(*CommittedFace, GridIndex);
|
||||
if (Vote == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FString Token = ResolveClassicFaceletToken(Vote->WinningNotation, Vote->WinningColorId);
|
||||
if (Token.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FaceTokens->Add(Token);
|
||||
}
|
||||
}
|
||||
|
||||
return OutFaceletState.U.Num() == 9
|
||||
&& OutFaceletState.R.Num() == 9
|
||||
&& OutFaceletState.F.Num() == 9
|
||||
&& OutFaceletState.D.Num() == 9
|
||||
&& OutFaceletState.L.Num() == 9
|
||||
&& OutFaceletState.B.Num() == 9;
|
||||
}
|
||||
|
||||
bool TryBuildClassicRecognitionStateSnapshot(
|
||||
const FHyperTwistVisionSessionConfig& SessionConfig,
|
||||
const FHyperTwistVisionReconstructionSession& Session,
|
||||
FHyperTwistPuzzleState& OutState,
|
||||
FHyperTwistStateSnapshot& OutSnapshot
|
||||
)
|
||||
{
|
||||
FHyperTwistClassicFaceletSnapshotState FaceletState;
|
||||
if (!TryBuildClassicFaceletSnapshotFromReconstruction(Session, FaceletState))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FString PayloadJson;
|
||||
if (!SerializeStruct(FaceletState, PayloadJson))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FHyperTwistPuzzleDefinitionRef Definition;
|
||||
Definition.PuzzleId = !Session.PuzzleId.IsEmpty() ? Session.PuzzleId : TEXT("cube/3x3x3");
|
||||
Definition.PuzzleFamily = EHyperTwistPuzzleFamily::ClassicCube;
|
||||
Definition.Dimension = 3;
|
||||
Definition.DefinitionVersion = TEXT("2026.05");
|
||||
Definition.NotationProfile = !SessionConfig.NotationProfile.IsEmpty()
|
||||
? SessionConfig.NotationProfile
|
||||
: TEXT("classic-wca");
|
||||
Definition.SizeVector = {3, 3, 3};
|
||||
|
||||
OutState = FHyperTwistPuzzleState();
|
||||
OutState.Definition = Definition;
|
||||
OutState.StateEncodingKind = EHyperTwistStateEncodingKind::Facelet;
|
||||
OutState.StateEncoding.EncodingProfile = FaceletState.EncodingProfile;
|
||||
OutState.StateEncoding.PayloadJson = PayloadJson;
|
||||
OutState.OrientationFrame.Reference = !Session.OrientationProfile.IsEmpty()
|
||||
? Session.OrientationProfile
|
||||
: TEXT("classic-target-face-net-v1");
|
||||
OutState.bIsSolved =
|
||||
IsUniformFaceletTokenArray(FaceletState.U)
|
||||
&& IsUniformFaceletTokenArray(FaceletState.R)
|
||||
&& IsUniformFaceletTokenArray(FaceletState.F)
|
||||
&& IsUniformFaceletTokenArray(FaceletState.D)
|
||||
&& IsUniformFaceletTokenArray(FaceletState.L)
|
||||
&& IsUniformFaceletTokenArray(FaceletState.B);
|
||||
OutState.Source = EHyperTwistStateSource::Recognition;
|
||||
OutState.CapturedAtUtc = FDateTime::UtcNow().ToIso8601();
|
||||
OutState.SourceConfidence = Session.ConfidenceRollup;
|
||||
OutState.SourceSessionId = Session.SessionId;
|
||||
OutState.Notes = TEXT("Phase 6R-C reconstruction session aggregate.");
|
||||
|
||||
OutSnapshot = FHyperTwistStateSnapshot();
|
||||
OutSnapshot.SnapshotId = FString::Printf(TEXT("recognition_reconstruction_%s"), *Session.SessionId);
|
||||
OutSnapshot.State = OutState;
|
||||
OutSnapshot.DerivedHash = FString::Printf(
|
||||
TEXT("recognition-reconstruction-%s-%d-%d"),
|
||||
*Session.SessionId,
|
||||
Session.TotalCommittedFaceCount,
|
||||
Session.TotalRevisionCount
|
||||
);
|
||||
return OutSnapshot.IsStructurallyValid();
|
||||
}
|
||||
|
||||
FHyperTwistVisionCalibrationProfile BuildDefaultClassicCubeCalibrationProfile(const FString& ProfileId)
|
||||
{
|
||||
FHyperTwistVisionCalibrationProfile Profile;
|
||||
|
|
@ -6078,6 +6536,7 @@ bool UHyperTwistTrainingSubsystem::OpenActiveRecognitionSession(FString& OutErro
|
|||
ActiveRecognitionSessionState.SessionConfig = SessionConfig;
|
||||
ActiveRecognitionSessionState.bHasPreviewResult = false;
|
||||
ActiveRecognitionSessionState.bHasCommitResult = false;
|
||||
ActiveRecognitionSessionState.ActiveReconstructionSession = FHyperTwistVisionReconstructionSession();
|
||||
ActiveRecognitionSessionState.LastPreviewResult = FHyperTwistVisionPreviewResult();
|
||||
ActiveRecognitionSessionState.LastCommitResult = FHyperTwistVisionCommitResult();
|
||||
RefreshRecognitionServiceHealth();
|
||||
|
|
@ -6231,16 +6690,70 @@ FHyperTwistVisionCommitResult UHyperTwistTrainingSubsystem::CommitActiveRecognit
|
|||
Result.Snapshot.State.SourceSessionId = NormalizedRequest.SessionId;
|
||||
}
|
||||
|
||||
bool bWasReconstructionRevision = false;
|
||||
if (HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(ActiveRecognitionSessionState.SessionConfig)
|
||||
&& Result.ObservedFace.IsStructurallyValid())
|
||||
{
|
||||
if (Result.CommittedUnit.IsEmpty())
|
||||
{
|
||||
Result.CommittedUnit = HyperTwistTrainingSubsystemInternal::NormalizeRecognitionFaceId(
|
||||
Result.ObservedFace.FaceId
|
||||
);
|
||||
}
|
||||
|
||||
Result.ReconstructionSession =
|
||||
HyperTwistTrainingSubsystemInternal::BuildClassicRecognitionReconstructionSession(
|
||||
ActiveRecognitionSessionState.SessionConfig,
|
||||
Result.SessionId,
|
||||
ActiveRecognitionSessionState.ActiveReconstructionSession,
|
||||
Result.ObservedFace,
|
||||
bWasReconstructionRevision
|
||||
);
|
||||
ActiveRecognitionSessionState.ActiveReconstructionSession = Result.ReconstructionSession;
|
||||
|
||||
TArray<FString> ProviderMissingUnits = Result.MissingUnits;
|
||||
Result.MissingUnits = Result.ReconstructionSession.MissingFaces;
|
||||
for (const FString& ProviderMissingUnit : ProviderMissingUnits)
|
||||
{
|
||||
Result.MissingUnits.AddUnique(ProviderMissingUnit);
|
||||
}
|
||||
|
||||
Result.Confidence = Result.ReconstructionSession.ConfidenceRollup;
|
||||
if (Result.ReconstructionSession.bHasCompleteClassicCubeNet)
|
||||
{
|
||||
FHyperTwistPuzzleState ReconstructedState;
|
||||
FHyperTwistStateSnapshot ReconstructedSnapshot;
|
||||
if (HyperTwistTrainingSubsystemInternal::TryBuildClassicRecognitionStateSnapshot(
|
||||
ActiveRecognitionSessionState.SessionConfig,
|
||||
Result.ReconstructionSession,
|
||||
ReconstructedState,
|
||||
ReconstructedSnapshot
|
||||
))
|
||||
{
|
||||
Result.Snapshot = ReconstructedSnapshot;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Snapshot = FHyperTwistStateSnapshot();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Snapshot = FHyperTwistStateSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
const FString ResolvedStage = !Result.CommittedUnit.IsEmpty()
|
||||
? Result.CommittedUnit
|
||||
: (!NormalizedRequest.TargetStage.IsEmpty() ? NormalizedRequest.TargetStage : NormalizedRequest.TargetFace);
|
||||
const bool bHasCorrectionSignals =
|
||||
Result.Warnings.Num() > 0 || Result.MissingUnits.Num() > 0;
|
||||
Result.Warnings.Num() > 0 || bWasReconstructionRevision;
|
||||
const bool bHasCommitSignal =
|
||||
!Result.CommittedUnit.IsEmpty()
|
||||
|| Result.Snapshot.IsStructurallyValid()
|
||||
|| Result.Snapshot.State.IsStructurallyValid()
|
||||
|| Result.Confidence > 0.0f;
|
||||
|| Result.Confidence > 0.0f
|
||||
|| Result.ReconstructionSession.IsStructurallyValid();
|
||||
|
||||
if (bHasCorrectionSignals)
|
||||
{
|
||||
|
|
@ -6327,6 +6840,48 @@ FHyperTwistVisionFinalizeResult UHyperTwistTrainingSubsystem::FinalizeActiveReco
|
|||
Result.FinalSnapshot.State.SourceSessionId = SessionId;
|
||||
}
|
||||
|
||||
if (HyperTwistTrainingSubsystemInternal::IsQbrClassicCubeRoute(ActiveRecognitionSessionState.SessionConfig))
|
||||
{
|
||||
Result.ReconstructionSession = ActiveRecognitionSessionState.ActiveReconstructionSession;
|
||||
if (Result.ReconstructionSession.IsStructurallyValid())
|
||||
{
|
||||
Result.ConfidenceRollup = Result.ReconstructionSession.ConfidenceRollup;
|
||||
}
|
||||
|
||||
if (Result.ReconstructionSession.IsStructurallyValid()
|
||||
&& Result.ReconstructionSession.bHasCompleteClassicCubeNet)
|
||||
{
|
||||
FHyperTwistPuzzleState ReconstructedState;
|
||||
FHyperTwistStateSnapshot ReconstructedSnapshot;
|
||||
if (HyperTwistTrainingSubsystemInternal::TryBuildClassicRecognitionStateSnapshot(
|
||||
ActiveRecognitionSessionState.SessionConfig,
|
||||
Result.ReconstructionSession,
|
||||
ReconstructedState,
|
||||
ReconstructedSnapshot
|
||||
))
|
||||
{
|
||||
Result.FinalState = ReconstructedState;
|
||||
Result.FinalSnapshot = ReconstructedSnapshot;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.FinalState = FHyperTwistPuzzleState();
|
||||
Result.FinalSnapshot = FHyperTwistStateSnapshot();
|
||||
Result.NormalizationWarnings.AddUnique(TEXT("recognition-reconstruction-unavailable"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.FinalState = FHyperTwistPuzzleState();
|
||||
Result.FinalSnapshot = FHyperTwistStateSnapshot();
|
||||
Result.NormalizationWarnings.AddUnique(
|
||||
Result.ReconstructionSession.IsStructurallyValid()
|
||||
? TEXT("recognition-reconstruction-incomplete")
|
||||
: TEXT("recognition-reconstruction-unavailable")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TArray<FHyperTwistReplayEvent> PendingRecognitionEvents;
|
||||
for (const FString& SeedJson : Result.ReplaySeedEventsJson)
|
||||
{
|
||||
|
|
@ -6433,6 +6988,7 @@ FHyperTwistVisionFinalizeResult UHyperTwistTrainingSubsystem::FinalizeActiveReco
|
|||
ActiveRecognitionSessionState.bSessionOpen = false;
|
||||
ActiveRecognitionSessionState.bHasFinalizeResult = true;
|
||||
ActiveRecognitionSessionState.LastFinalizeResult = Result;
|
||||
ActiveRecognitionSessionState.ActiveReconstructionSession = FHyperTwistVisionReconstructionSession();
|
||||
ActiveRecognitionSessionState.ActiveSessionId.Reset();
|
||||
ActiveRecognitionSessionState.LastUpdatedAtUtc = FDateTime::UtcNow().ToIso8601();
|
||||
++ActiveRecognitionSessionState.FinalizedSessionCount;
|
||||
|
|
@ -6480,6 +7036,7 @@ bool UHyperTwistTrainingSubsystem::CloseActiveRecognitionSession(FString& OutErr
|
|||
|
||||
ActiveRecognitionSessionState.bSessionOpen = false;
|
||||
ActiveRecognitionSessionState.ActiveSessionId.Reset();
|
||||
ActiveRecognitionSessionState.ActiveReconstructionSession = FHyperTwistVisionReconstructionSession();
|
||||
ActiveRecognitionSessionState.LastUpdatedAtUtc = FDateTime::UtcNow().ToIso8601();
|
||||
ActiveRecognitionSessionState.LastError.Reset();
|
||||
RefreshRecognitionServiceHealth();
|
||||
|
|
|
|||
|
|
@ -173,6 +173,208 @@ struct FHyperTwistVisionFaceObservation
|
|||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistVisionStickerVoteTally
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString Notation;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ColorId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 VoteCount = 0;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
return !Notation.IsEmpty() && !ColorId.IsEmpty() && VoteCount > 0;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistVisionReconstructionStickerVote
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 GridIndex = -1;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 GridRow = -1;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 GridColumn = -1;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString WinningNotation;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString WinningColorId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 WinningVoteCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 TotalVoteCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHadConflict = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistVisionStickerVoteTally> Tallies;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (GridIndex < 0
|
||||
|| GridRow < 0
|
||||
|| GridColumn < 0
|
||||
|| WinningNotation.IsEmpty()
|
||||
|| WinningColorId.IsEmpty()
|
||||
|| WinningVoteCount <= 0
|
||||
|| TotalVoteCount <= 0
|
||||
|| Tallies.Num() <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FHyperTwistVisionStickerVoteTally& Tally : Tallies)
|
||||
{
|
||||
if (!Tally.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistVisionCommittedFaceState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString FaceId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString LatestObservationId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString OrderingProfile = TEXT("3x3-row-major");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 ObservationCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 RevisionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasConflicts = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistVisionObservedSticker> LatestOrderedStickers;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistVisionReconstructionStickerVote> StickerVotes;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (FaceId.IsEmpty()
|
||||
|| LatestObservationId.IsEmpty()
|
||||
|| LatestOrderedStickers.Num() != 9
|
||||
|| StickerVotes.Num() != 9
|
||||
|| ObservationCount <= 0
|
||||
|| RevisionCount < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FHyperTwistVisionObservedSticker& Sticker : LatestOrderedStickers)
|
||||
{
|
||||
if (!Sticker.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const FHyperTwistVisionReconstructionStickerVote& Vote : StickerVotes)
|
||||
{
|
||||
if (!Vote.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistVisionReconstructionSession
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString SessionId;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString PuzzleId = TEXT("cube/3x3x3");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString ReconstructionProfile = TEXT("classic-face-vote-v1");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FString OrientationProfile = TEXT("classic-target-face-net-v1");
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bUsesObservationVoting = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasStableOrientation = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 OrientationStateOrdinal = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 TotalCommittedFaceCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
int32 TotalRevisionCount = 0;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
float ConfidenceRollup = 0.0f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
bool bHasCompleteClassicCubeNet = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FHyperTwistVisionCommittedFaceState> CommittedFaces;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> MissingFaces;
|
||||
|
||||
bool IsStructurallyValid() const
|
||||
{
|
||||
if (SessionId.IsEmpty() || PuzzleId.IsEmpty() || ReconstructionProfile.IsEmpty() || OrientationProfile.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FHyperTwistVisionCommittedFaceState& Face : CommittedFaces)
|
||||
{
|
||||
if (!Face.IsStructurallyValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return TotalCommittedFaceCount >= 0 && TotalRevisionCount >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FHyperTwistVisionSessionConfig
|
||||
{
|
||||
|
|
@ -310,6 +512,9 @@ struct FHyperTwistVisionCommitResult
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionFaceObservation ObservedFace;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionReconstructionSession ReconstructionSession;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> Warnings;
|
||||
|
||||
|
|
@ -331,6 +536,9 @@ struct FHyperTwistVisionFinalizeResult
|
|||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistStateSnapshot FinalSnapshot;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionReconstructionSession ReconstructionSession;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
TArray<FString> ReplaySeedEventsJson;
|
||||
|
||||
|
|
|
|||
|
|
@ -1885,6 +1885,9 @@ struct FHyperTwistTrainingRecognitionSessionState
|
|||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionFinalizeResult LastFinalizeResult;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
|
||||
FHyperTwistVisionReconstructionSession ActiveReconstructionSession;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,245 @@
|
|||
// Copyright HyperTwist, Inc. All Rights Reserved.
|
||||
|
||||
#include "Misc/AutomationTest.h"
|
||||
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "HyperTwistCore/HyperTwistCoreLibrary.h"
|
||||
#include "HyperTwistCore/HyperTwistCoreTypes.h"
|
||||
#include "HyperTwistTraining/HyperTwistTrainingSubsystem.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "UObject/UnrealType.h"
|
||||
|
||||
#if WITH_AUTOMATION_TESTS
|
||||
|
||||
namespace HyperTwistRubixCubeSolverPhase6RCTestInternal
|
||||
{
|
||||
FHyperTwistTrainingDeck MakeRecognitionDeck()
|
||||
{
|
||||
FHyperTwistTrainingDeck Deck;
|
||||
Deck.DeckId = TEXT("phase6r-c/rubix-cube-solver-recognition");
|
||||
Deck.Title = TEXT("Phase 6R-C Rubix Cube Solver Recognition");
|
||||
Deck.DeliveryModes = {
|
||||
EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
|
||||
EHyperTwistTrainingDeliveryMode::CoachReviewed
|
||||
};
|
||||
|
||||
FHyperTwistTrainingCase TrainingCase;
|
||||
TrainingCase.CaseId = TEXT("phase6r-c-case");
|
||||
TrainingCase.PuzzleId = TEXT("cube/3x3x3");
|
||||
TrainingCase.PromptKind = EHyperTwistTrainingPromptKind::Recognition;
|
||||
TrainingCase.PromptLabel = TEXT("Phase 6R-C Recognition");
|
||||
TrainingCase.AllowedDeliveryModes = {
|
||||
EHyperTwistTrainingDeliveryMode::RecognitionAssisted,
|
||||
EHyperTwistTrainingDeliveryMode::CoachReviewed
|
||||
};
|
||||
Deck.Cases = {TrainingCase};
|
||||
return Deck;
|
||||
}
|
||||
|
||||
void ForceMockRecognitionClient(UHyperTwistTrainingSubsystem* TrainingSubsystem)
|
||||
{
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (FStrProperty* RecognitionClientKindProperty = FindFProperty<FStrProperty>(
|
||||
UHyperTwistTrainingSubsystem::StaticClass(),
|
||||
TEXT("RecognitionClientKind")
|
||||
))
|
||||
{
|
||||
RecognitionClientKindProperty->SetPropertyValue_InContainer(TrainingSubsystem, TEXT("mock"));
|
||||
}
|
||||
}
|
||||
|
||||
UHyperTwistTrainingSubsystem* MakeRecognitionSubsystem(const FString& SessionId)
|
||||
{
|
||||
UGameInstance* GameInstance = NewObject<UGameInstance>(GetTransientPackage());
|
||||
if (GameInstance == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem = NewObject<UHyperTwistTrainingSubsystem>(GameInstance);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ForceMockRecognitionClient(TrainingSubsystem);
|
||||
const FHyperTwistTrainingRunState RunState = TrainingSubsystem->StartTrainingRunFromDeck(
|
||||
MakeRecognitionDeck(),
|
||||
TEXT("phase6r-c-user"),
|
||||
SessionId,
|
||||
EHyperTwistTrainingDeliveryMode::RecognitionAssisted
|
||||
);
|
||||
return RunState.IsStructurallyValid() ? TrainingSubsystem : nullptr;
|
||||
}
|
||||
|
||||
void SubmitPreviewFrame(UHyperTwistTrainingSubsystem* TrainingSubsystem, const FString& FaceId, const int32 FrameOrdinal)
|
||||
{
|
||||
FHyperTwistVisionFrameEnvelope Frame;
|
||||
Frame.FrameOrdinal = FrameOrdinal;
|
||||
Frame.TargetFaceHint = FaceId;
|
||||
TrainingSubsystem->SubmitActiveRecognitionFrame(Frame);
|
||||
}
|
||||
|
||||
FHyperTwistVisionCommitResult CommitFace(
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem,
|
||||
const FString& FaceId,
|
||||
const int32 FrameOrdinal
|
||||
)
|
||||
{
|
||||
SubmitPreviewFrame(TrainingSubsystem, FaceId, FrameOrdinal);
|
||||
|
||||
FHyperTwistVisionCommitRequest Request;
|
||||
Request.TargetFace = FaceId;
|
||||
return TrainingSubsystem->CommitActiveRecognitionObservation(Request);
|
||||
}
|
||||
|
||||
const FHyperTwistVisionCommittedFaceState* FindCommittedFace(
|
||||
const FHyperTwistVisionReconstructionSession& Session,
|
||||
const FString& FaceId
|
||||
)
|
||||
{
|
||||
return Session.CommittedFaces.FindByPredicate(
|
||||
[&FaceId](const FHyperTwistVisionCommittedFaceState& Candidate)
|
||||
{
|
||||
return Candidate.FaceId.Equals(FaceId, ESearchCase::IgnoreCase);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistRubixCubeSolverPhase6RCCommitBuildsReconstructionSessionTest,
|
||||
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.C.CommitBuildsReconstructionSession",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistRubixCubeSolverPhase6RCCommitBuildsReconstructionSessionTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem =
|
||||
HyperTwistRubixCubeSolverPhase6RCTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-c-commit-session"));
|
||||
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-C."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FHyperTwistVisionCommitResult Result =
|
||||
HyperTwistRubixCubeSolverPhase6RCTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 1);
|
||||
|
||||
TestTrue(TEXT("The commit result must carry a structurally valid reconstruction session."), Result.ReconstructionSession.IsStructurallyValid());
|
||||
TestEqual(TEXT("The first face commit must keep one committed face."), Result.ReconstructionSession.CommittedFaces.Num(), 1);
|
||||
TestEqual(TEXT("The first face commit must report five missing faces."), Result.ReconstructionSession.MissingFaces.Num(), 5);
|
||||
TestEqual(TEXT("The reconstruction session must keep the committed face id."), Result.ReconstructionSession.CommittedFaces[0].FaceId, TEXT("F"));
|
||||
TestFalse(TEXT("An incomplete reconstruction session must not expose a full snapshot yet."), Result.Snapshot.IsStructurallyValid());
|
||||
|
||||
const FHyperTwistTrainingRecognitionSessionState SessionState =
|
||||
TrainingSubsystem->GetActiveRecognitionSessionState();
|
||||
TestTrue(TEXT("The active recognition state must keep the same reconstruction session."), SessionState.ActiveReconstructionSession.IsStructurallyValid());
|
||||
TestEqual(TEXT("The active recognition state must track one committed face."), SessionState.ActiveReconstructionSession.CommittedFaces.Num(), 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistRubixCubeSolverPhase6RCRevisionLedgerTest,
|
||||
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.C.RevisionLedger",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistRubixCubeSolverPhase6RCRevisionLedgerTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem =
|
||||
HyperTwistRubixCubeSolverPhase6RCTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-c-revision-session"));
|
||||
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-C."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
HyperTwistRubixCubeSolverPhase6RCTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 1);
|
||||
const FHyperTwistVisionCommitResult RevisionResult =
|
||||
HyperTwistRubixCubeSolverPhase6RCTestInternal::CommitFace(TrainingSubsystem, TEXT("F"), 2);
|
||||
|
||||
const FHyperTwistVisionCommittedFaceState* FaceState =
|
||||
HyperTwistRubixCubeSolverPhase6RCTestInternal::FindCommittedFace(RevisionResult.ReconstructionSession, TEXT("F"));
|
||||
TestNotNull(TEXT("The revised reconstruction session must still track the front face."), FaceState);
|
||||
if (FaceState == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TestEqual(TEXT("A second face commit must increment the observation count."), FaceState->ObservationCount, 2);
|
||||
TestEqual(TEXT("A second face commit must increment the revision count."), FaceState->RevisionCount, 1);
|
||||
TestEqual(TEXT("The revision ledger must preserve one total revision in the session."), RevisionResult.ReconstructionSession.TotalRevisionCount, 1);
|
||||
TestEqual(TEXT("The first sticker vote must record two votes after a recommit."), FaceState->StickerVotes[0].WinningVoteCount, 2);
|
||||
TestFalse(TEXT("A revised but incomplete reconstruction session must still not expose a full snapshot."), RevisionResult.Snapshot.IsStructurallyValid());
|
||||
|
||||
const FHyperTwistTrainingRecognitionSessionState SessionState =
|
||||
TrainingSubsystem->GetActiveRecognitionSessionState();
|
||||
TestEqual(TEXT("A recommit must count as one correction event."), SessionState.CorrectionEventCount, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
IMPLEMENT_SIMPLE_AUTOMATION_TEST(
|
||||
FHyperTwistRubixCubeSolverPhase6RCFinalizeBuildsClassicNetTest,
|
||||
"HyperTwist.Permissive.RubixCubeSolver.Phase6R.C.FinalizeBuildsClassicNet",
|
||||
EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter
|
||||
)
|
||||
|
||||
bool FHyperTwistRubixCubeSolverPhase6RCFinalizeBuildsClassicNetTest::RunTest(const FString& Parameters)
|
||||
{
|
||||
UHyperTwistTrainingSubsystem* TrainingSubsystem =
|
||||
HyperTwistRubixCubeSolverPhase6RCTestInternal::MakeRecognitionSubsystem(TEXT("phase6r-c-finalize-session"));
|
||||
TestNotNull(TEXT("The recognition subsystem must be constructed for Phase 6R-C."), TrainingSubsystem);
|
||||
if (TrainingSubsystem == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const TArray<FString> FaceOrder = {
|
||||
TEXT("U"),
|
||||
TEXT("R"),
|
||||
TEXT("F"),
|
||||
TEXT("D"),
|
||||
TEXT("L"),
|
||||
TEXT("B")
|
||||
};
|
||||
for (int32 FaceOrdinal = 0; FaceOrdinal < FaceOrder.Num(); ++FaceOrdinal)
|
||||
{
|
||||
HyperTwistRubixCubeSolverPhase6RCTestInternal::CommitFace(
|
||||
TrainingSubsystem,
|
||||
FaceOrder[FaceOrdinal],
|
||||
FaceOrdinal + 1
|
||||
);
|
||||
}
|
||||
|
||||
const FHyperTwistVisionFinalizeResult Result = TrainingSubsystem->FinalizeActiveRecognitionSession();
|
||||
|
||||
TestTrue(TEXT("The finalized session must carry a structurally valid reconstruction session."), Result.ReconstructionSession.IsStructurallyValid());
|
||||
TestTrue(TEXT("The finalized session must carry a complete classic cube net."), Result.ReconstructionSession.bHasCompleteClassicCubeNet);
|
||||
TestTrue(TEXT("A complete reconstruction session must emit a structurally valid final state."), Result.FinalState.IsStructurallyValid());
|
||||
TestTrue(TEXT("A complete reconstruction session must emit a structurally valid final snapshot."), Result.FinalSnapshot.IsStructurallyValid());
|
||||
TestTrue(TEXT("The reconstructed final state should resolve as solved for the mock color net."), UHyperTwistCoreLibrary::IsSolved(Result.FinalState));
|
||||
TestTrue(TEXT("The bounded finalize path should not need normalization warnings for a complete mock route."), Result.NormalizationWarnings.IsEmpty());
|
||||
|
||||
FHyperTwistClassicFaceletSnapshotState FaceletState;
|
||||
TestTrue(
|
||||
TEXT("The final facelet payload must deserialize."),
|
||||
FJsonObjectConverter::JsonObjectStringToUStruct(Result.FinalState.StateEncoding.PayloadJson, &FaceletState, 0, 0)
|
||||
);
|
||||
TestEqual(TEXT("The up face must keep nine stickers."), FaceletState.U.Num(), 9);
|
||||
TestEqual(TEXT("The right face must keep nine stickers."), FaceletState.R.Num(), 9);
|
||||
TestEqual(TEXT("The front face must keep the green face token."), FaceletState.F[0], TEXT("G"));
|
||||
TestEqual(TEXT("The back face must keep the blue face token."), FaceletState.B[0], TEXT("B"));
|
||||
|
||||
const FHyperTwistTrainingRecognitionSessionState SessionState =
|
||||
TrainingSubsystem->GetActiveRecognitionSessionState();
|
||||
TestFalse(TEXT("The active recognition session must be closed after finalization."), SessionState.bSessionOpen);
|
||||
TestTrue(TEXT("The last finalize result must keep the complete reconstruction session."), SessionState.LastFinalizeResult.ReconstructionSession.bHasCompleteClassicCubeNet);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -94,7 +94,7 @@ Status update on `2026-05-20`:
|
|||
|
||||
- the current canonical repo-row portfolio count is `75`
|
||||
- all `75` current portfolio rows now have explicit root-tracker coverage
|
||||
- the current implemented-row count is `26`
|
||||
- the current implemented-row count is `27`
|
||||
- the four later restrictive clean-room rows now implemented after the original `2026-05-13` truth
|
||||
block are:
|
||||
- `cubing/alg.js`
|
||||
|
|
@ -105,6 +105,8 @@ Status update on `2026-05-20`:
|
|||
- `HactarCE/Hyperspeedcube`
|
||||
- the sixth additional implemented row is the same-day bounded permissive partial row:
|
||||
- `kkoomen/qbr`
|
||||
- the seventh additional implemented row is the same-day bounded permissive partial row:
|
||||
- `vivaansinghvi07/rubix-cube-solver`
|
||||
- the earlier first-party packet block is now confirmed landed in current code through:
|
||||
- `d4f4ad3` `Add primary coach orchestration entry lane`
|
||||
- `1860f14` `Add provider-backed recognition sidecar client`
|
||||
|
|
@ -114,8 +116,11 @@ Status update on `2026-05-20`:
|
|||
- `HactarCE/Hyperspeedcube` remains partially incorporated
|
||||
- the bounded permissive `Phase 6R-B` `kkoomen/qbr` classic-cube recognition calibration and
|
||||
ordered face-observation packet is now landed in current code
|
||||
- `kkoomen/qbr` remains partially incorporated; the current next bounded move is a source-backed
|
||||
`Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` preparation/control pass, not a new
|
||||
- `kkoomen/qbr` remains partially incorporated
|
||||
- the bounded permissive `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` committed-face
|
||||
reconstruction and face-vote replacement packet is now landed in current code
|
||||
- `vivaansinghvi07/rubix-cube-solver` remains partially incorporated; the current next bounded
|
||||
move is a source-backed `Phase 6R-D` `roice3/MagicTile` preparation/control pass, not a new
|
||||
restrictive packet by default
|
||||
- use the repo-row README census, the portfolio standing refresh backfill, and the `2R-A`
|
||||
ownership contract for the current queue after that correction
|
||||
|
|
@ -123,16 +128,17 @@ Status update on `2026-05-20`:
|
|||
This file now also preserves the current truth that future models must not lose:
|
||||
|
||||
- current curated HyperTwist shallow-eval set: `75` repos
|
||||
- currently verified live/implemented in checked `UnrealHyperTwist` surfaces: `26`
|
||||
- permissive live lanes: `15`
|
||||
- currently verified live/implemented in checked `UnrealHyperTwist` surfaces: `27`
|
||||
- permissive live lanes: `16`
|
||||
- boundary-sensitive live lanes: `6`
|
||||
- restrictive live lanes: `5`
|
||||
|
||||
The fifteen permissive live lanes are:
|
||||
The sixteen permissive live lanes are:
|
||||
|
||||
- `Aarav2709/KubeTimr`
|
||||
- `HactarCE/Hyperspeedcube`
|
||||
- `kkoomen/qbr`
|
||||
- `vivaansinghvi07/rubix-cube-solver`
|
||||
- `apache/echarts`
|
||||
- `abunickabhi/5style-Trainer`
|
||||
- `google/model-viewer`
|
||||
|
|
@ -215,8 +221,8 @@ Current practical interpretation:
|
|||
- the landed `PostHog/posthog` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_D_POSTHOG_CONTROL_PLANE_IMPLEMENTATION_2026-05-13.md`
|
||||
- the landed `screenpipe/screenpipe` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_E_SCREENPIPE_CAPTURE_HISTORY_IMPLEMENTATION_2026-05-13.md`
|
||||
- the landed `remotion-dev/remotion` boundary-sensitive widening packet now lives in `docs/HYPERTWIST_PHASE_4R_PACKET_4R_F_REMOTION_MEDIA_EXPORT_IMPLEMENTATION_2026-05-13.md`
|
||||
- the next bounded move is a source-backed `Phase 6R-C`
|
||||
`vivaansinghvi07/rubix-cube-solver` preparation/control pass
|
||||
- the next bounded move is a source-backed `Phase 6R-D` `roice3/MagicTile`
|
||||
preparation/control pass
|
||||
|
||||
Companion docs:
|
||||
|
||||
|
|
@ -748,9 +754,18 @@ Approved working posture:
|
|||
- HyperTwist may use the checked repo directly under the upstream `MIT` posture
|
||||
- preserve the upstream `MIT` license text and copyright notice:
|
||||
- `Copyright (c) 2023 Vivaan Singhvi`
|
||||
- keep the row queued as the recognition companion candidate after `kkoomen/qbr`
|
||||
- preserve the row as the bounded recognition companion lane after `kkoomen/qbr`
|
||||
- if later implementation ships or copies bundled third-party frontend assets, capture and preserve
|
||||
those assets' own upstream license/provenance rather than flattening them into the repo-level `MIT`
|
||||
- the first bounded retained slice is now landed in current code:
|
||||
- committed-face reconstruction session
|
||||
- face-vote replacement ledger
|
||||
- final classic-net shaping above aggregated committed faces
|
||||
- keep the row only partially incorporated by default:
|
||||
- browser/webcam shell remains deferred
|
||||
- broad solve explanation or recommendation shell remains deferred
|
||||
- any redistribution of `frontend/lib/twistysim.min.js` still requires preserved or replaced
|
||||
upstream provenance/license
|
||||
|
||||
### `tentone/rubix-solver`
|
||||
|
||||
|
|
@ -1258,10 +1273,10 @@ Approved working posture:
|
|||
permission to transplant the whole Rust application shell
|
||||
- do not let this row absorb non-Euclidean topology ownership that remains queued for
|
||||
`roice3/MagicTile`
|
||||
- after the landed puzzle-catalog slice and the landed bounded `qbr` calibration slice, the next
|
||||
queue head should move to a source-backed `Phase 6R-C`
|
||||
`vivaansinghvi07/rubix-cube-solver` preparation/control pass rather than pretending the entire
|
||||
retained recognition family is already closed
|
||||
- after the landed puzzle-catalog slice, the landed bounded `qbr` calibration slice, and the
|
||||
landed bounded `rubix-cube-solver` reconstruction slice, the next queue head should move to a
|
||||
source-backed `Phase 6R-D` `roice3/MagicTile` preparation/control pass rather than pretending
|
||||
the entire retained topology family is already closed
|
||||
|
||||
### `coqui-ai/TTS`
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
# HyperTwist Phase 6R-C rubix-cube-solver reconstruction implementation packet
|
||||
|
||||
Created on `2026-05-20`
|
||||
|
||||
## Status
|
||||
|
||||
- first-party HyperTwist packet
|
||||
- bounded permissive `Phase 6R-C` implementation slice
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet lands the first narrower bounded slice from the retained
|
||||
`vivaansinghvi07/rubix-cube-solver` recognition-companion row.
|
||||
|
||||
The landed slice is:
|
||||
|
||||
- first-party classic-cube committed-face reconstruction session plus face-vote replacement ledger
|
||||
|
||||
It is not:
|
||||
|
||||
- a full `rubix-cube-solver` row transplant
|
||||
- a browser/webcam shell packet
|
||||
- a broad solve-explanation packet
|
||||
- a bundled `twistysim.min.js` redistribution packet
|
||||
- a `MagicTile` topology packet
|
||||
|
||||
## Current authority basis
|
||||
|
||||
This implementation packet stands on:
|
||||
|
||||
- `docs/HYPERTWIST_PHASE_0R_PACKET_0R_A_EVALUATION_2026-05-12.md`
|
||||
- `docs/HYPERTWIST_PHASE_2R_PACKET_2R_A_OWNERSHIP_AND_ACCEPTANCE_CONTRACT_2026-05-13.md`
|
||||
- `docs/REPO_LICENSE_TRACKING.md`
|
||||
- `docs/arch/HYPERTWIST_PHASE6R_C_RUBIX_CUBE_SOLVER_RECONSTRUCTION_PREPARATION_PACKET_2026-05-20.md`
|
||||
|
||||
The retained owner remains:
|
||||
|
||||
- `vivaansinghvi07/rubix-cube-solver`
|
||||
|
||||
The granted family remains bounded to:
|
||||
|
||||
- committed-face reconstruction flow
|
||||
- face-vote replacement and correction ledger
|
||||
- bounded final net shaping above committed faces
|
||||
|
||||
This packet lands only the first narrower family in that granted set.
|
||||
|
||||
## Landed scope
|
||||
|
||||
The current code now owns a retained classic-cube reconstruction contract through:
|
||||
|
||||
- retained recognition contract types for:
|
||||
- sticker vote tallies
|
||||
- reconstruction sticker votes
|
||||
- committed face state
|
||||
- reconstruction session
|
||||
- recognition session state tracking for the active reconstruction session
|
||||
- classic-cube reconstruction-session aggregation and face-vote replacement in
|
||||
`UHyperTwistTrainingSubsystem`
|
||||
- classic-cube missing-face accounting and confidence rollup derived from the bounded
|
||||
reconstruction session
|
||||
- final classic facelet snapshot and net shaping when the six-face reconstruction becomes complete
|
||||
- sample reconstruction-session routing in `UHyperTwistContractLibrary`
|
||||
- ordered classic-face preview and commit payload correction in `UHyperTwistMockVisionClient`
|
||||
- focused automation coverage in:
|
||||
- `HyperTwistRubixCubeSolverPhase6RCReconstructionContractTest.cpp`
|
||||
|
||||
## Why this is still intentionally bounded
|
||||
|
||||
This packet replaces the thinner commit/finalize reconstruction heuristics with a reconstruction
|
||||
session owned by first-party code, but it does not widen into the neighboring retained families.
|
||||
|
||||
Still deferred:
|
||||
|
||||
- browser/webcam shell
|
||||
- broad solve explanation or recommendation shell
|
||||
- bundled `twistysim.min.js` redistribution
|
||||
- generalized solver backend ownership
|
||||
- `MagicTile` or broader topology widening
|
||||
|
||||
## Validation
|
||||
|
||||
Build validation:
|
||||
|
||||
- `Build.bat UnrealHyperTwistEditor Win64 Development -Project='C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.uproject' -WaitMutex -NoHotReloadFromIDE`
|
||||
|
||||
Focused automation validation:
|
||||
|
||||
- `Automation RunTests HyperTwist.Permissive.RubixCubeSolver.Phase6R.C`
|
||||
|
||||
Regression automation validation:
|
||||
|
||||
- `Automation RunTests HyperTwist.Permissive.Qbr.Phase6R.B`
|
||||
- `Automation RunTests HyperTwist.Permissive.Hyperspeedcube.Phase6R.A`
|
||||
- `Automation RunTests HyperTwist.CleanRoom.CubeDesk`
|
||||
|
||||
Expected covered tests:
|
||||
|
||||
- `CommitBuildsReconstructionSession`
|
||||
- `RevisionLedger`
|
||||
- `FinalizeBuildsClassicNet`
|
||||
- `CalibrationSessionConfig`
|
||||
- `PreviewObservation`
|
||||
- `CommitObservation`
|
||||
- `CatalogLookup`
|
||||
- `SampleDefinition`
|
||||
- `TrainingRunDefinition`
|
||||
- existing `CubeDesk` clean-room regression suite
|
||||
|
||||
## Queue effect
|
||||
|
||||
This packet consumes the current `Phase 6R-C` implementation slice.
|
||||
|
||||
`vivaansinghvi07/rubix-cube-solver` remains only partially incorporated:
|
||||
|
||||
- landed now:
|
||||
- committed-face reconstruction session
|
||||
- face-vote replacement ledger
|
||||
- final classic-net shaping above aggregated committed faces
|
||||
- still deferred:
|
||||
- browser/webcam shell
|
||||
- broad solve explanation or recommendation shell
|
||||
- bundled `twistysim.min.js` redistribution
|
||||
|
||||
The next clean move is not another immediate `rubix-cube-solver` widening by default.
|
||||
|
||||
The next queue head should be:
|
||||
|
||||
- a source-backed `Phase 6R-D` `roice3/MagicTile` preparation/control pass
|
||||
|
||||
Keep the future legal sequencing guard visible when this row widens again:
|
||||
|
||||
- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing its
|
||||
upstream provenance/license
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
# HyperTwist Phase 6R-C rubix-cube-solver reconstruction preparation packet
|
||||
|
||||
Created on `2026-05-20`
|
||||
|
||||
## Status
|
||||
|
||||
- historical same-day preparation authority
|
||||
- bounded post-Phase-`6R-B` preparation slice
|
||||
- the first bounded implementation slice now lands separately under:
|
||||
- `docs/arch/HYPERTWIST_PHASE6R_C_RUBIX_CUBE_SOLVER_RECONSTRUCTION_IMPLEMENTATION_PACKET_2026-05-20.md`
|
||||
|
||||
## Purpose
|
||||
|
||||
This packet freezes the next widening order after the landed `Phase 6R-B`
|
||||
`kkoomen/qbr` classic-cube recognition calibration and ordered face-observation packet.
|
||||
|
||||
The open task is:
|
||||
|
||||
- define the first bounded source-backed widening packet for
|
||||
`vivaansinghvi07/rubix-cube-solver` as the retained `Phase 6R-C` reconstruction companion lane
|
||||
|
||||
It is not:
|
||||
|
||||
- a full `rubix-cube-solver` row transplant
|
||||
- a browser/webcam shell packet
|
||||
- a broad solve-explanation packet
|
||||
- a direct redistribution packet for the bundled `frontend/lib/twistysim.min.js` asset
|
||||
- a `MagicTile` topology packet
|
||||
- a general recognition-provider widening packet
|
||||
|
||||
## Current authority basis
|
||||
|
||||
This preparation packet stands on already-closed authority:
|
||||
|
||||
- `docs/HYPERTWIST_PHASE_0R_PACKET_0R_A_EVALUATION_2026-05-12.md`
|
||||
- `docs/HYPERTWIST_PHASE_2R_PACKET_2R_A_OWNERSHIP_AND_ACCEPTANCE_CONTRACT_2026-05-13.md`
|
||||
- `docs/REPO_LICENSE_TRACKING.md`
|
||||
- `docs/arch/HYPERTWIST_PHASE6R_B_QBR_CALIBRATED_FACE_OBSERVATION_IMPLEMENTATION_PACKET_2026-05-20.md`
|
||||
|
||||
The key accepted routing facts are:
|
||||
|
||||
- `vivaansinghvi07/rubix-cube-solver` is the retained recognition companion donor immediately after
|
||||
the landed `6R-B` slice
|
||||
- earliest widening route is `Phase 6R / Packet 6R-C`
|
||||
- the retained donor value there is:
|
||||
- committed-face reconstruction flow
|
||||
- face-vote replacement and correction ledger
|
||||
- bounded final net shaping above committed faces
|
||||
- ownership denied there is:
|
||||
- do not let the repo absorb the full browser shell
|
||||
- do not widen into broad solve explanation or generic solver UI transport
|
||||
- do not copy or redistribute the bundled `frontend/lib/twistysim.min.js` asset without
|
||||
preserving or replacing its upstream provenance/license
|
||||
- do not let the row absorb the queued `MagicTile` topology lane
|
||||
|
||||
## Why this is now the next packet
|
||||
|
||||
The earlier `Phase 6R-B` queue head is already landed:
|
||||
|
||||
- retained classic-cube recognition calibration contract
|
||||
- ordered face-observation contract
|
||||
|
||||
That means the live queue advances to:
|
||||
|
||||
- `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver`
|
||||
|
||||
with the adjacent retained rows still ordered behind it:
|
||||
|
||||
- `Phase 6R-D` `roice3/MagicTile`
|
||||
- the bounded speech-input / voice sidecar set
|
||||
|
||||
## Required result
|
||||
|
||||
The source-backed control pass for this packet is now complete.
|
||||
|
||||
The first actual `6R-C` implementation packet should:
|
||||
|
||||
- use the retained `0R-A` and `2R-A` authority surfaces plus the inspected
|
||||
`rubix-cube-solver` source basis
|
||||
- define one bounded retained slice from `rubix-cube-solver`
|
||||
- keep the slice inside the accepted recognition-companion ownership
|
||||
- widen only the first reconstruction family that can stand on its own without dragging in the full
|
||||
browser shell, bundled frontend asset redistribution, or the next queued topology row
|
||||
- explicitly state which neighboring retained rows stay closed in that packet
|
||||
|
||||
## Source-backed retained basis
|
||||
|
||||
The queue-head decision is now source-backed rather than README-only.
|
||||
|
||||
Inspected retained donor basis:
|
||||
|
||||
- `README.md`
|
||||
- `backend/cv.py`
|
||||
- `backend/server.py`
|
||||
- `frontend/src/script.js`
|
||||
|
||||
Inspected first-party receiving basis:
|
||||
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistRecognition/HyperTwistRecognitionTypes.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistRecognition/HyperTwistMockVisionClient.cpp`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp`
|
||||
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp`
|
||||
|
||||
## Narrowed 6R-C slice decision
|
||||
|
||||
The first widening slice is now fixed as:
|
||||
|
||||
1. classic-cube committed-face reconstruction session plus face-vote replacement ledger
|
||||
|
||||
not as the broader browser or explanation shell.
|
||||
|
||||
That narrowed slice should cover:
|
||||
|
||||
- retained reconstruction-session contract types
|
||||
- committed-face state and sticker-vote aggregation
|
||||
- bounded correction-friendly replacement ledger for recommitted faces
|
||||
- classic-cube missing-face accounting above committed observations
|
||||
- final facelet snapshot and classic-net shaping when the six-face reconstruction becomes complete
|
||||
- subsystem and contract-library routing for that bounded reconstruction session
|
||||
- focused automation coverage for:
|
||||
- reconstruction-session accumulation
|
||||
- revision ledger behavior
|
||||
- final classic-net shaping
|
||||
|
||||
## Deferred neighboring families
|
||||
|
||||
The first `6R-C` implementation packet must keep these capability families closed:
|
||||
|
||||
- browser/webcam shell
|
||||
- broad solve explanation
|
||||
- bundled `twistysim.min.js` redistribution
|
||||
- generalized solver transport or remote-service ownership
|
||||
- `MagicTile` or broader topology widening
|
||||
- speech-input / voice sidecar ownership
|
||||
|
||||
Why this slice is first:
|
||||
|
||||
- the donor exposes its strongest narrow retained seam at committed-face reconstruction and
|
||||
correction-friendly replacement, not at the full browser shell
|
||||
- current first-party recognition code already had session, preview, and commit envelopes plus the
|
||||
landed `qbr` ordered face-observation contract, but it still lacked an owned reconstruction
|
||||
session that could accumulate corrected committed faces into a final classic-cube net
|
||||
- broad explanation or frontend widening would prematurely absorb neighboring rows and bundled
|
||||
asset obligations
|
||||
|
||||
## Out of scope for the first 6R-C packet
|
||||
|
||||
- full browser/webcam UI transplant
|
||||
- broad solve explanation or recommendation shell
|
||||
- bundled `frontend/lib/twistysim.min.js` redistribution
|
||||
- generalized solver backend ownership
|
||||
- `MagicTile` or broader topology widening
|
||||
|
||||
## Queue-adjacent legal sequencing guards
|
||||
|
||||
Do not omit the already-recorded legal caveats when the neighboring retained rows later come up:
|
||||
|
||||
- `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver`
|
||||
- preserve or replace the bundled `frontend/lib/twistysim.min.js` with its upstream
|
||||
provenance/license intact before redistribution if any packet copies or ships that dependency
|
||||
- `Phase 6R-D` `roice3/MagicTile`
|
||||
- keep non-Euclidean topology ownership bounded to that later packet rather than letting the
|
||||
current recognition companion lane absorb it by convenience
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- the packet records why the live queue now advances from landed `6R-B` to `6R-C`
|
||||
- the packet promotes `rubix-cube-solver` as the current queue head without widening the
|
||||
neighboring retained rows prematurely
|
||||
- the packet records that the first `6R-C` widening slice is committed-face reconstruction session
|
||||
plus face-vote replacement ledger only
|
||||
- the packet states exact out-of-scope families for the first `6R-C` pass
|
||||
- the packet keeps the `twistysim.min.js` caveat visible in later queue sequencing
|
||||
|
||||
## Validation checklist
|
||||
|
||||
1. confirm the landed `Phase 6R-B` packet is now the consumed prior queue head
|
||||
2. confirm the retained reconstruction seam in `rubix-cube-solver` narrows cleanly to committed
|
||||
face aggregation and replacement ledger behavior
|
||||
3. confirm the next packet is framed as bounded source-backed widening rather than README-only
|
||||
routing
|
||||
|
||||
That is the packet.
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
Status update on `2026-05-20`:
|
||||
|
||||
- the canonical HyperTwist repo-row portfolio is now treated as `75` rows, not `71`
|
||||
- currently implemented rows are now `26`, not `20`
|
||||
- currently implemented rows are now `27`, not `20`
|
||||
- four of the additional current implemented rows are the later-landed restrictive clean-room lanes:
|
||||
- `cubing/alg.js`
|
||||
- `cubing/twisty.js`
|
||||
|
|
@ -13,6 +13,8 @@ Status update on `2026-05-20`:
|
|||
- `HactarCE/Hyperspeedcube`
|
||||
- the sixth additional current implemented row is the same-day bounded permissive partial row:
|
||||
- `kkoomen/qbr`
|
||||
- the seventh additional current implemented row is the same-day bounded permissive partial row:
|
||||
- `vivaansinghvi07/rubix-cube-solver`
|
||||
- the current next bounded move is **not** another restrictive packet by default
|
||||
- the earlier first-party packet block that was still being treated as next is now confirmed landed:
|
||||
- `d4f4ad3` `Add primary coach orchestration entry lane`
|
||||
|
|
@ -29,8 +31,14 @@ Status update on `2026-05-20`:
|
|||
- `kkoomen/qbr` remains partially incorporated; webcam UI shell, bundled-font redistribution,
|
||||
multilingual solve shell, and multi-face reconstruction/correction/explanation families stay
|
||||
deferred
|
||||
- the current next bounded move is a source-backed `Phase 6R-C`
|
||||
`vivaansinghvi07/rubix-cube-solver` preparation/control pass
|
||||
- the generic source-backed `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` control pass is now
|
||||
consumed
|
||||
- the bounded permissive `Phase 6R-C` `vivaansinghvi07/rubix-cube-solver` committed-face
|
||||
reconstruction and face-vote replacement packet is now landed in current code
|
||||
- `vivaansinghvi07/rubix-cube-solver` remains partially incorporated; browser/webcam shell, broad
|
||||
solve explanation, and bundled `twistysim.min.js` redistribution stay deferred
|
||||
- the current next bounded move is a source-backed `Phase 6R-D` `roice3/MagicTile`
|
||||
preparation/control pass
|
||||
- the repo-row implementation queue is now live from that `Phase 6R-A` entry point rather than
|
||||
waiting on another first-party packet
|
||||
|
||||
|
|
@ -58,8 +66,8 @@ Donor-strength rule:
|
|||
- route difficulty changes how retained value may enter the product, not whether it may win technically
|
||||
|
||||
- current shallow-eval set: `75` repos
|
||||
- currently verified live/implemented in checked Unreal surfaces: `26`
|
||||
- permissive live lanes: `15`
|
||||
- currently verified live/implemented in checked Unreal surfaces: `27`
|
||||
- permissive live lanes: `16`
|
||||
- boundary-sensitive live lanes: `6`
|
||||
- restrictive live lanes: `5`
|
||||
- the restrictive landed lanes are:
|
||||
|
|
@ -69,7 +77,7 @@ Donor-strength rule:
|
|||
- `HactarCE/2x2x2x2-Scrambler`
|
||||
- `kash/cubedesk`
|
||||
- each restrictive landed lane is to be treated as properly clean-roomed and implemented afterward
|
||||
- `Phase 0R` is now closed for the remaining `49` non-live rows
|
||||
- `Phase 0R` is now closed for the remaining `48` non-live rows
|
||||
- `Phase 1R` is now closed as the retained-set contract and handoff overhaul
|
||||
- `Phase 2R` is now closed as the retained-set ownership and acceptance packet sequence
|
||||
- `Phase 3R-A` is now closed as the landed `Aarav2709/KubeTimr` timer subsystem widening packet
|
||||
|
|
@ -89,17 +97,16 @@ Current routing truth:
|
|||
|
||||
- retained rows total: `68`
|
||||
- discarded from the active retained set: `3`
|
||||
- active non-live implementation-board rows: `37`
|
||||
- active non-live implementation-board rows: `36`
|
||||
- retained benchmark, oracle, or clean-room-later rows outside the active implementation board: `9`
|
||||
|
||||
The next bounded move is a source-backed `Phase 6R-C`
|
||||
`vivaansinghvi07/rubix-cube-solver` preparation/control pass.
|
||||
The next bounded move is a source-backed `Phase 6R-D` `roice3/MagicTile`
|
||||
preparation/control pass.
|
||||
|
||||
Queue interpretation after that packet:
|
||||
|
||||
- then continue with the retained repo-row queue
|
||||
- highest current repo-row queue pressure sits in:
|
||||
- `vivaansinghvi07/rubix-cube-solver`
|
||||
- `roice3/MagicTile`
|
||||
- the bounded speech-input / voice sidecar set
|
||||
- `HactarCE/Hyperspeedcube` remains a partially landed row rather than a closed row:
|
||||
|
|
@ -118,13 +125,19 @@ Queue interpretation after that packet:
|
|||
- webcam UI shell
|
||||
- bundled-font redistribution and multilingual solve shell
|
||||
- multi-face reconstruction / correction / explanation
|
||||
- after the `Phase 6R-B` implementation packet, keep the adjacent legal sequencing guards visible:
|
||||
- `kkoomen/qbr`
|
||||
- do not copy or redistribute `src/assets/arial-unicode-ms.ttf` without separate
|
||||
confirmation or replacement in any future widening that would ship that asset
|
||||
- `vivaansinghvi07/rubix-cube-solver` remains a partially landed row rather than a closed row:
|
||||
- landed:
|
||||
- committed-face reconstruction session
|
||||
- face-vote replacement ledger
|
||||
- final classic-net shaping above aggregated committed faces
|
||||
- still deferred:
|
||||
- browser/webcam shell
|
||||
- broad solve explanation or recommendation shell
|
||||
- bundled `twistysim.min.js` redistribution
|
||||
- after the `Phase 6R-C` implementation packet, keep the future legal sequencing guard visible:
|
||||
- `vivaansinghvi07/rubix-cube-solver`
|
||||
- do not copy or redistribute `frontend/lib/twistysim.min.js` without preserving or replacing
|
||||
its upstream provenance/license
|
||||
its upstream provenance/license in any future widening that would ship that asset
|
||||
- keep the optional `Phase 3R-G` browser comparison lane deferred unless the landed primary browser
|
||||
spatial stack exposes a real gap
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue