Fix native progression routes around scenery and narrow depth views
This commit is contained in:
@@ -3,14 +3,23 @@ param([Parameter(Mandatory)][string]$ApkPath,[Parameter(Mandatory)][string]$Evid
|
||||
# This suite never pushes a save or injects completed stages. Every checkpoint is read from the APK.
|
||||
function Board($State,[int]$Stage) { return @($State.puzzleStates | Where-Object stageId -eq $Stage)[0] }
|
||||
function Counts($Board) { return (@($Board.groups | ForEach-Object {$_.ballIds.Count} | Sort-Object) -join ',') }
|
||||
function Orbit([string]$Name,$CurrentBoard,[switch]$Pitch) {
|
||||
function Orbit([string]$Name,$CurrentBoard,[switch]$Pitch,[int]$YawPixels=120) {
|
||||
if(!$CurrentBoard){$saved=Save "$Name-view";$CurrentBoard=if($saved.puzzleStates.Count){$saved.puzzleStates[-1]}else{$saved.latestFreePlay}}
|
||||
$points=@($CurrentBoard.balls|ForEach-Object{,(BallPoint $CurrentBoard $_.id)})
|
||||
foreach($y in @(300,700)){foreach($x in @(650,900,1150,1400,1650)){
|
||||
if(@($points|Where-Object{[Math]::Pow($_[0]-$x,2)+[Math]::Pow($_[1]-$y,2)-lt22500}).Count-eq0){
|
||||
$endX=if($Pitch){$x}else{$x+120};$endY=if($Pitch){$y+120}else{$y}
|
||||
$endX=if($Pitch){$x}else{$x+$YawPixels};$endY=if($Pitch){$y+120}else{$y}
|
||||
if($endY-gt750){continue}
|
||||
Step $Name @('shell','input','swipe',"$x","$y","$endX","$endY",'350');return
|
||||
if($YawPixels-ne120-and!$Pitch){
|
||||
# Latch the real >14px orbit drag, then reverse to the small net yaw.
|
||||
try{
|
||||
NativePointer @($x,$y,81,1024);Start-Sleep -Milliseconds 100
|
||||
NativePointer @(($x+[Math]::Sign($YawPixels)*20),$y,81,1024);Start-Sleep -Milliseconds 100
|
||||
NativePointer @($endX,$endY,81,1024);Start-Sleep -Milliseconds 100
|
||||
}finally{NativePointer @($endX,$endY,81,0)}
|
||||
Step $Name @() 500
|
||||
}else{Step $Name @('shell','input','swipe',"$x","$y","$endX","$endY",'350')}
|
||||
return
|
||||
}
|
||||
}}
|
||||
throw 'No safe blank orbit start in the actual board view.'
|
||||
@@ -34,11 +43,43 @@ function RevealBall([int]$Stage,[string]$Id,[string]$Name) {
|
||||
}
|
||||
throw "No safe first-hit view for actual ball $Id within a full native orbit."
|
||||
}
|
||||
function LiftMergeRoute([int]$Stage,[string]$Name,$Board,[int]$SourceCount=0,[int]$TargetCount=0) {
|
||||
$eligible=@($Board.groups|Where-Object {
|
||||
$candidate=$_
|
||||
($SourceCount-eq0-or$candidate.ballIds.Count-eq$SourceCount)-and@($Board.groups|Where-Object {$_.id-ne$candidate.id-and($TargetCount-eq0-or$_.ballIds.Count-eq$TargetCount)}).Count-gt0
|
||||
}|Sort-Object {$_.ballIds.Count})
|
||||
$id=$null
|
||||
foreach($group in $eligible){
|
||||
$low=@($Board.balls|Where-Object {$group.ballIds-contains$_.id-and$_.position.y-lt2}|Sort-Object {$_.position.y})
|
||||
if($low.Count){$id=$low[0].id;break}
|
||||
}
|
||||
if(!$id){return $null}
|
||||
$board=RevealBall $Stage $id "$Name-reveal"
|
||||
$point=BallPoint $board $id;$end=@($point[0],($point[1]-120))
|
||||
if(!(WorldSafe $end)){
|
||||
$unchanged=Save "$Name-no-safe-upward-endpoint"
|
||||
$actual=Board $unchanged $Stage
|
||||
Check "$Name-unsafe-endpoint-board-unchanged" ($null-ne$actual-and(Same $board $actual)) 'No native touch: the complete revealed board, counts and manual pose remain exact'
|
||||
return [pscustomobject]@{State=$unchanged;Before=$board;SourceId=$id}
|
||||
}
|
||||
$after=NativeMoveAndHold $Name $board $id $end
|
||||
$actual=Board $after $Stage;if(!$actual){$actual=$after.latestFreePlay}
|
||||
Check "$Name-detour-manual-camera-preserved" (Same $board.camera $actual.camera) 'Every detour outcome retains the exact confirmed manual pose'
|
||||
if($actual.groups.Count-lt$board.groups.Count){AssertNativeMerge $Name $board $actual $id $TargetCount;return [pscustomobject]@{State=$after;Before=$board;SourceId=$id}}
|
||||
Check "$Name-detour-membership-preserved" (Same $board.groups $actual.groups) 'An upward route never injects a split or merge'
|
||||
Check "$Name-detour-not-counted" (Same $board.countedBallIds $actual.countedBallIds) 'An actual detour does not count friends'
|
||||
if(Same $board.balls $actual.balls){
|
||||
$commands.Add(@{command='Upward detour produced no observed ball movement; continue actual view search, never count as merge';sourceId=$id;utc=[DateTime]::UtcNow.ToString('o')})
|
||||
}else{AssertNativeTranslation $Name $board $actual $id}
|
||||
return [pscustomobject]@{State=$after;Before=$board;SourceId=$id}
|
||||
}
|
||||
function MergeReal([int]$Stage,[string]$Name,[int]$SourceCount=0,[int]$TargetCount=0) {
|
||||
for($attempt=0;$attempt-lt16;$attempt++) {
|
||||
$before=Save "$Name-before-$attempt"; $board=Board $before $Stage
|
||||
if($null-eq$board){return $before}
|
||||
$oldCount=$board.groups.Count
|
||||
# If the initial view has no safe pair, route upward before pitching further.
|
||||
# Reproject the actual result within this attempt; the original bound stays sixteen.
|
||||
for($projection=0;$projection-lt2;$projection++){
|
||||
$rotation=[Numerics.Quaternion]::new($board.camera.rotation.x,$board.camera.rotation.y,$board.camera.rotation.z,$board.camera.rotation.w)
|
||||
$pairs=@(foreach($source in $board.groups){foreach($target in $board.groups){if($source.id-eq$target.id-or($SourceCount-gt0-and$source.ballIds.Count-ne$SourceCount)-or($TargetCount-gt0-and$target.ballIds.Count-ne$TargetCount)){continue};foreach($sid in $source.ballIds){foreach($tid in $target.ballIds){
|
||||
$s=@($board.balls|Where-Object id -eq $sid)[0].position; $t=@($board.balls|Where-Object id -eq $tid)[0].position
|
||||
@@ -52,6 +93,19 @@ function MergeReal([int]$Stage,[string]$Name,[int]$SourceCount=0,[int]$TargetCou
|
||||
$targetPoint=BallPoint $board $candidate.tid
|
||||
if(WorldSafe @(($targetPoint[0]+$offset),$targetPoint[1])){$pair=$candidate;break}
|
||||
}
|
||||
if($pair-or$attempt-ne0-or$projection-ne0){break}
|
||||
$routed=LiftMergeRoute $Stage "$Name-upward-route" $board $SourceCount $TargetCount
|
||||
if(!$routed){break}
|
||||
$routedBoard=Board $routed.State $Stage
|
||||
if(!$routedBoard-or$routedBoard.groups.Count-lt$routed.Before.groups.Count){
|
||||
$actual=if($routedBoard){$routedBoard}else{$routed.State.latestFreePlay}
|
||||
if(!$routedBoard){Check "$Name-route-completed-stage" ($routed.State.completedStageIds-contains$Stage-and$actual.sourceStageId-eq$Stage) 'A missing puzzle requires actual completion and its own free-play board'}
|
||||
AssertNativeMerge "$Name-route-actual" $routed.Before $actual $routed.SourceId $TargetCount
|
||||
return $routed.State
|
||||
}
|
||||
$board=$routedBoard
|
||||
}
|
||||
$oldCount=$board.groups.Count
|
||||
if($pair) {
|
||||
$end=BallPoint $board $pair.tid
|
||||
# Try a short lateral contact offset, then orbit to expose another depth plane if blocked.
|
||||
@@ -63,7 +117,30 @@ function MergeReal([int]$Stage,[string]$Name,[int]$SourceCount=0,[int]$TargetCou
|
||||
}
|
||||
$board=$newBoard
|
||||
}
|
||||
Orbit "$Name-orbit-$attempt" $board -Pitch:($attempt%4-eq3)
|
||||
$fineYaw=0
|
||||
if(!$pair){
|
||||
# A coarse orbit can skip the narrow depth window of an otherwise safe pair.
|
||||
# Choose a small improving yaw, then read the actual projection next attempt.
|
||||
foreach($candidate in $pairs){
|
||||
if($candidate.depth-lt.9-or$candidate.depth-gt1.2-or!(BallClear $board $candidate.sid)){continue}
|
||||
$target=BallPoint $board $candidate.tid
|
||||
if(!(WorldSafe @(($target[0]+$offset),$target[1]))){continue}
|
||||
$s=@($board.balls|Where-Object id -eq $candidate.sid)[0].position;$t=@($board.balls|Where-Object id -eq $candidate.tid)[0].position
|
||||
$separation=[Numerics.Vector3]::new($s.x-$t.x,$s.y-$t.y,$s.z-$t.z)
|
||||
$best=$candidate.depth
|
||||
foreach($pixels in @(-8,8)){
|
||||
$predicted=[Numerics.Quaternion]::CreateFromAxisAngle([Numerics.Vector3]::UnitY,($pixels*.25*[Math]::PI/180))*$rotation
|
||||
$depth=[Math]::Abs([Numerics.Vector3]::Transform($separation,[Numerics.Quaternion]::Inverse($predicted)).Z)
|
||||
if($depth-lt$best){$best=$depth;$fineYaw=$pixels}
|
||||
}
|
||||
if($fineYaw){break}
|
||||
}
|
||||
}
|
||||
if($fineYaw){
|
||||
Orbit "$Name-fine-orbit-$attempt" $board -YawPixels $fineYaw
|
||||
$view=Board (Save "$Name-fine-orbit-$attempt-state") $Stage
|
||||
Check "$Name-fine-orbit-preserves-board-$attempt" ($null-ne$view-and(Same $board.balls $view.balls)-and(Same $board.groups $view.groups)-and(Same $board.collectedBallIds $view.collectedBallIds)-and(Same $board.countedBallIds $view.countedBallIds)-and!(Same $board.camera $view.camera)) 'Actual manual pose changed; no ball, membership, collection or count edits'
|
||||
}else{Orbit "$Name-orbit-$attempt" $board -Pitch:($attempt%4-eq3)}
|
||||
}
|
||||
throw "Native merge bound reached for Stage $Stage; all actual attempts retained."
|
||||
}
|
||||
|
||||
@@ -44,14 +44,11 @@ function NativeMoveAndHold([string]$Name,$Board,[string]$Id,[int[]]$End) {
|
||||
Step $Name @() 2500
|
||||
return Save "$Name-dropped"
|
||||
}
|
||||
function AssertNativeMerge([string]$Name,$Before,$After,[string]$SourceId,[int]$TargetCount=0) {
|
||||
function AssertNativeTranslation([string]$Name,$Before,$After,[string]$SourceId) {
|
||||
$idsBefore=@($Before.balls.id|Sort-Object);$idsAfter=@($After.balls.id|Sort-Object)
|
||||
Check "$Name-exact-original-ids" (Same $idsBefore $idsAfter) 'No lost, duplicated or substituted IDs after actual native input'
|
||||
Check "$Name-manual-camera-preserved" (Same $Before.camera $After.camera) 'Automatic assistance never persists as the manual camera'
|
||||
$source=@($Before.groups|Where-Object {$_.ballIds-contains$SourceId})[0]
|
||||
$merged=@($After.groups|Where-Object {$_.ballIds-contains$SourceId})[0]
|
||||
$targets=@($Before.groups|Where-Object {$_.id-ne$source.id-and@($_.ballIds|Where-Object {$merged.ballIds-notcontains$_}).Count-eq0})
|
||||
Check "$Name-actual-source-and-target-membership" ($targets.Count-eq1-and$merged.ballIds.Count-eq($source.ballIds.Count+$targets[0].ballIds.Count)-and@($source.ballIds|Where-Object {$merged.ballIds-notcontains$_}).Count-eq0-and($TargetCount-eq0-or$targets[0].ballIds.Count-eq$TargetCount)) 'Grabbed source plus exactly one actual foreign group; requested target size retained'
|
||||
$firstBefore=@($Before.balls|Where-Object id -eq $SourceId)[0].position
|
||||
$firstAfter=@($After.balls|Where-Object id -eq $SourceId)[0].position
|
||||
$delta=[Numerics.Vector3]::new($firstAfter.x-$firstBefore.x,$firstAfter.y-$firstBefore.y,$firstAfter.z-$firstBefore.z)
|
||||
@@ -63,6 +60,13 @@ function AssertNativeMerge([string]$Name,$Before,$After,[string]$SourceId,[int]$
|
||||
Check "$Name-rigid-source-$($ball.id)" (($movement-$delta).Length()-lt.0001) 'Every source member shares the same actual translation'
|
||||
}else{Check "$Name-stationary-foreign-$($ball.id)" (Same $ball.position $actual) 'Unselected foreign balls retain exact saved positions'}
|
||||
}
|
||||
}
|
||||
function AssertNativeMerge([string]$Name,$Before,$After,[string]$SourceId,[int]$TargetCount=0) {
|
||||
AssertNativeTranslation $Name $Before $After $SourceId
|
||||
$source=@($Before.groups|Where-Object {$_.ballIds-contains$SourceId})[0]
|
||||
$merged=@($After.groups|Where-Object {$_.ballIds-contains$SourceId})[0]
|
||||
$targets=@($Before.groups|Where-Object {$_.id-ne$source.id-and@($_.ballIds|Where-Object {$merged.ballIds-notcontains$_}).Count-eq0})
|
||||
Check "$Name-actual-source-and-target-membership" ($targets.Count-eq1-and$merged.ballIds.Count-eq($source.ballIds.Count+$targets[0].ballIds.Count)-and@($source.ballIds|Where-Object {$merged.ballIds-notcontains$_}).Count-eq0-and($TargetCount-eq0-or$targets[0].ballIds.Count-eq$TargetCount)) 'Grabbed source plus exactly one actual foreign group; requested target size retained'
|
||||
$distance=[double]::PositiveInfinity
|
||||
foreach($sid in $source.ballIds){foreach($tid in $targets[0].ballIds){
|
||||
$s=@($After.balls|Where-Object id -eq $sid)[0].position;$t=@($After.balls|Where-Object id -eq $tid)[0].position
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
param([Parameter(Mandatory)][string]$EvidenceDir,[Parameter(Mandatory)][string]$Fixture,[string]$CommonSource=(Join-Path $PSScriptRoot 'final-common.ps1'),[string]$ProgressionSource=(Join-Path $PSScriptRoot 'exercise-final-progression.ps1'))
|
||||
param([Parameter(Mandatory)][string]$EvidenceDir,[Parameter(Mandatory)][string]$Fixture,[string]$CommonSource=(Join-Path $PSScriptRoot 'final-common.ps1'),[string]$ProgressionSource=(Join-Path $PSScriptRoot 'exercise-final-progression.ps1'),[string]$BlockedFixture,[string]$LiftBefore,[string]$LiftAfter,[string]$FineFixture)
|
||||
$ErrorActionPreference='Stop'
|
||||
New-Item -ItemType Directory -Path $EvidenceDir -Force|Out-Null
|
||||
$checks=[Collections.Generic.List[object]]::new()
|
||||
@@ -22,6 +22,7 @@ function Adb([string[]]$Arguments){return 'Physical size: 1080x2400'}
|
||||
function Step([string]$Name,[string[]]$Arguments=@(),[int]$Delay=500){$commands.Add(@{name=$Name;arguments=$Arguments})}
|
||||
function Save([string]$Name){return $raw}
|
||||
function global:NativeMoveAndHold([string]$Name,$Board,[string]$Id,[int[]]$End){$commands.Add(@{nativeHeldMove=$true;id=$Id;end=$End});return $raw}
|
||||
function global:LiftMergeRoute([int]$Stage,[string]$Name,$Board,[int]$SourceCount=0,[int]$TargetCount=0){return [pscustomobject]@{State=$raw;Before=$Board;SourceId=$Board.balls[0].id}}
|
||||
try{
|
||||
foreach($name in @('BallPoint','WorldSafe','BallClear','Same')){ReadFunction $CommonSource $name}
|
||||
foreach($name in @('Board','Orbit','MergeReal')){ReadFunction $ProgressionSource $name}
|
||||
@@ -44,6 +45,8 @@ try{
|
||||
$script:touches.Clear();$rejected=$false;try{NativeMoveAndHold 'changed-preview' $board 's05-b04' $end|Out-Null}catch{$rejected=$true}
|
||||
Record 'persisted-preview-is-rejected-and-contact-released' ($rejected-and$script:touches.Count-eq3-and$script:touches[-1][3]-eq0) $script:touches.ToArray()
|
||||
ReadFunction $CommonSource 'AssertNativeMerge'
|
||||
$commonAst=[Management.Automation.Language.Parser]::ParseFile($CommonSource,[ref]$null,[ref]$null)
|
||||
if($commonAst.Find({param($n)$n-is[Management.Automation.Language.FunctionDefinitionAst]-and$n.Name-eq'AssertNativeTranslation'},$true)){ReadFunction $CommonSource 'AssertNativeTranslation'}
|
||||
$success=$board|ConvertTo-Json -Depth 20|ConvertFrom-Json
|
||||
$source=@($board.groups|Where-Object {$_.ballIds-contains's05-b04'})[0]
|
||||
$target=@($board.groups|Where-Object {$_.ballIds-notcontains's05-b04'})[0]
|
||||
@@ -81,7 +84,126 @@ try{
|
||||
$rejected=$false;try{AssertNativeMerge "offline-$kind" $board $bad 's05-b01' $targetSize}catch{$rejected=$true}
|
||||
Record 'invalid-group-translation-or-target-size-is-rejected' $rejected $kind
|
||||
}
|
||||
if($BlockedFixture){
|
||||
if(!$LiftBefore-or!$LiftAfter){throw 'Blocked-route controls require actual root-observed lift before/after saves'}
|
||||
$raw=Get-Content -LiteralPath $BlockedFixture -Raw|ConvertFrom-Json
|
||||
$blocked=Board $raw 18
|
||||
if(!$blocked-or$blocked.balls.Count-ne8-or$blocked.groups.Count-ne2){throw 'Requires actual Stage18 failed 7+1 save'}
|
||||
$commands.Clear();$script:routeCalls=0
|
||||
function Save([string]$Name){return $raw}
|
||||
function global:NativeMoveAndHold([string]$Name,$Board,[string]$Id,[int[]]$End){$commands.Add(@{nativeHeldMove=$true;id=$Id;end=$End});return $raw}
|
||||
function global:LiftMergeRoute([int]$Stage,[string]$Name,$Board,[int]$SourceCount=0,[int]$TargetCount=0){$script:routeCalls++;$commands.Add(@{route=$true});return [pscustomobject]@{State=$raw;Before=$Board;SourceId='s18-b04'}}
|
||||
$bound=$false;try{MergeReal 18 'replay-actual-blocked18'|Out-Null}catch{$bound=$_.Exception.Message-match'Native merge bound reached'}
|
||||
$orbits=@($commands|Where-Object {$_.arguments.Count-ge7-and$_.arguments[2]-eq'swipe'})
|
||||
Record 'actual-blocked18-uses-one-detour-with-original-attempt-bound' ($bound-and$script:routeCalls-eq1-and$orbits.Count-eq16-and$commands[0].route) @{boundedFailureRetained=$bound;detours=$script:routeCalls;orbitAttempts=$orbits.Count;routeBeforeFirstOrbit=$commands[0].route}
|
||||
foreach($kind in @('missing-board-wrong-free','missing-board-without-completion','merged-without-contact')){
|
||||
$routeState=$raw|ConvertTo-Json -Depth 20|ConvertFrom-Json
|
||||
if($kind-eq'merged-without-contact'){
|
||||
(Board $routeState 18).groups=@([pscustomobject]@{id='unproven';ballIds=@($blocked.balls.id)})
|
||||
@((Board $routeState 18).balls|Where-Object id -eq 's18-b04')[0].position.y+=.1
|
||||
}
|
||||
else{
|
||||
$routeState.puzzleStates=@($routeState.puzzleStates|Where-Object stageId -ne 18)
|
||||
if($kind-eq'missing-board-wrong-free'){$routeState.completedStageIds+=18}
|
||||
else{$routeState.latestFreePlay=$blocked|ConvertTo-Json -Depth 20|ConvertFrom-Json;$routeState.latestFreePlay|Add-Member sourceStageId 18}
|
||||
}
|
||||
function global:LiftMergeRoute([int]$Stage,[string]$Name,$Board,[int]$SourceCount=0,[int]$TargetCount=0){return [pscustomobject]@{State=$routeState;Before=$Board;SourceId='s18-b04'}}
|
||||
$errorText=$null;try{MergeReal 18 "invalid-caller-route-$kind"|Out-Null}catch{$errorText=$_.Exception.Message}
|
||||
$expectedCheck=if($kind-eq'merged-without-contact'){'real-three-dimensional-contact'}else{'route-completed-stage'}
|
||||
Record 'caller-rejects-unproven-route-completion-or-contact' ($errorText-match$expectedCheck) @{kind=$kind;error=$errorText}
|
||||
}
|
||||
if($FineFixture){
|
||||
function global:LiftMergeRoute {return $null}
|
||||
function Save([string]$Name){return $raw|ConvertTo-Json -Depth 20|ConvertFrom-Json}
|
||||
foreach($mutation in @('none','ball','membership','collection','count','camera-no-op')){
|
||||
$raw=Get-Content -LiteralPath $FineFixture -Raw|ConvertFrom-Json
|
||||
$commands.Clear();$script:fineMoves=0;$script:orbitContacts=[Collections.Generic.List[object]]::new();$script:orbitStart=$null;$script:orbitLast=0;$script:orbitDragged=$false
|
||||
function NativePointer([int[]]$Contacts){
|
||||
$script:orbitContacts.Add(@($Contacts))
|
||||
if($Contacts[3]-eq0){$script:orbitStart=$null;return}
|
||||
if($null-eq$script:orbitStart){$script:orbitStart=$Contacts[0];$script:orbitLast=$Contacts[0];$script:orbitDragged=$false;return}
|
||||
$script:orbitDragged=$script:orbitDragged-or[Math]::Abs($Contacts[0]-$script:orbitStart)-ge14
|
||||
if($script:orbitDragged-and$mutation-ne'camera-no-op'){
|
||||
$b=Board $raw 18
|
||||
$rotation=[Numerics.Quaternion]::new($b.camera.rotation.x,$b.camera.rotation.y,$b.camera.rotation.z,$b.camera.rotation.w)
|
||||
$changed=[Numerics.Quaternion]::CreateFromAxisAngle([Numerics.Vector3]::UnitY,(($Contacts[0]-$script:orbitLast)*.25*[Math]::PI/180))*$rotation
|
||||
$b.camera.rotation=[pscustomobject]@{x=$changed.X;y=$changed.Y;z=$changed.Z;w=$changed.W}
|
||||
}
|
||||
$script:orbitLast=$Contacts[0]
|
||||
}
|
||||
function Step([string]$Name,[string[]]$Arguments=@(),[int]$Delay=500){
|
||||
$commands.Add(@{name=$Name;arguments=$Arguments})
|
||||
$b=Board $raw 18
|
||||
switch($mutation){
|
||||
'ball'{$b.balls[0].position.x+=.1}
|
||||
'membership'{$b.groups[0].ballIds+= 's18-b01'}
|
||||
'collection'{$b.collectedBallIds+= 's18-b04'}
|
||||
'count'{$b.countedBallIds+= 's18-b04'}
|
||||
}
|
||||
}
|
||||
# Counterfactual camera/rigid-contact control derived from the exact failed pose.
|
||||
# This proves the verifier oracle and never represents a native completion.
|
||||
function global:NativeMoveAndHold([string]$Name,$Board,[string]$Id,[int[]]$End){
|
||||
$script:fineMoves++
|
||||
$after=$raw|ConvertTo-Json -Depth 20|ConvertFrom-Json;$b=Board $after 18
|
||||
$source=@($b.groups|Where-Object {$_.ballIds-contains$Id})[0]
|
||||
$s=@($b.balls|Where-Object id -eq $Id)[0].position;$t=@($b.balls|Where-Object id -eq 's18-b04')[0].position
|
||||
$delta=[Numerics.Vector3]::new($t.x+1-$s.x,$t.y-$s.y,$t.z-$s.z)
|
||||
foreach($ball in $b.balls|Where-Object {$source.ballIds-contains$_.id}){$ball.position.x+=$delta.X;$ball.position.y+=$delta.Y;$ball.position.z+=$delta.Z}
|
||||
$b.groups=@([pscustomobject]@{id='offline-contact';ballIds=@($b.balls.id)})
|
||||
return $after
|
||||
}
|
||||
$errorText=$null;$value=$null
|
||||
try{$value=MergeReal 18 "fine-control-$mutation" 7 1}catch{$errorText=$_.Exception.Message}
|
||||
$contacts=$script:orbitContacts.ToArray()
|
||||
$smallYaw=$contacts.Count-eq4-and$contacts[0][3]-eq1024-and$contacts[1][3]-eq1024-and$contacts[2][3]-eq1024-and$contacts[3][3]-eq0-and[Math]::Abs($contacts[1][0]-$contacts[0][0])-eq20-and[Math]::Abs($contacts[2][0]-$contacts[0][0])-eq8-and$contacts[3][0]-eq$contacts[2][0]
|
||||
$passed=if($mutation-eq'none'){$null-eq$errorText-and$script:fineMoves-eq1-and(Board $value 18).groups.Count-eq1}else{$errorText-match'fine-orbit-preserves-board'-and$script:fineMoves-eq0}
|
||||
Record 'fine-yaw-from-actual-near-window-retains-strict-oracle' ($passed-and$smallYaw) @{mutation=$mutation;contacts=$contacts;commands=$commands.ToArray();nativeMockMoves=$script:fineMoves;error=$errorText;scope='Offline counterfactual geometry control with actual 14px drag latch from failed pose'}
|
||||
}
|
||||
function Step([string]$Name,[string[]]$Arguments=@(),[int]$Delay=500){$commands.Add(@{name=$Name;arguments=$Arguments})}
|
||||
}
|
||||
ReadFunction $CommonSource 'AssertNativeTranslation'
|
||||
ReadFunction $ProgressionSource 'LiftMergeRoute'
|
||||
$raw=Get-Content -LiteralPath $LiftBefore -Raw|ConvertFrom-Json
|
||||
$lifted=Get-Content -LiteralPath $LiftAfter -Raw|ConvertFrom-Json
|
||||
$board=Board $raw 18;$script:liftRequests=[Collections.Generic.List[object]]::new()
|
||||
function RevealBall([int]$Stage,[string]$Id,[string]$Name){return $board}
|
||||
function global:NativeMoveAndHold([string]$Name,$Board,[string]$Id,[int[]]$End){$script:liftRequests.Add(@{sourceId=$Id;end=$End});return $lifted}
|
||||
$actual=LiftMergeRoute 18 'replay-actual-observed-lift' $board
|
||||
$point=BallPoint $board 's18-b04'
|
||||
Record 'actual-root-lift-save-pair-passes-rigid-ID-camera-controls' ($script:liftRequests.Count-eq1-and$script:liftRequests[0].sourceId-eq's18-b04'-and$script:liftRequests[0].end[0]-eq$point[0]-and$script:liftRequests[0].end[1]-eq($point[1]-120)-and(Same $actual.State $lifted)-and(Same $actual.Before $board)-and$actual.SourceId-eq's18-b04') 'Uses root actual before/drop saves; no synthetic native success or completion injection'
|
||||
foreach($kind in @('wrong-target-size','foreign-ball-moved','camera-changed','missing-ID','no-op-camera-changed')){
|
||||
$script:liftRequests.Clear();$lifted=Get-Content -LiteralPath $LiftAfter -Raw|ConvertFrom-Json;$targetSize=0
|
||||
switch($kind){
|
||||
'wrong-target-size'{$targetSize=4}
|
||||
'foreign-ball-moved'{(Board $lifted 18).balls[0].position.x+=.1}
|
||||
'camera-changed'{(Board $lifted 18).camera.distance+=.1}
|
||||
'missing-ID'{(Board $lifted 18).balls=@((Board $lifted 18).balls|Where-Object id -ne 's18-b01')}
|
||||
'no-op-camera-changed'{(Board $lifted 18).balls=$board.balls;(Board $lifted 18).camera.distance+=.1}
|
||||
}
|
||||
$rejected=$false;$value=$null;try{$value=LiftMergeRoute 18 "invalid-route-$kind" $board 0 $targetSize}catch{$rejected=$true}
|
||||
$passed=if($kind-eq'wrong-target-size'){$null-eq$value-and$script:liftRequests.Count-eq0}else{$rejected}
|
||||
Record 'invalid-route-result-or-ineligible-target-is-rejected' $passed $kind
|
||||
}
|
||||
$board=Board (Get-Content -LiteralPath $BlockedFixture -Raw|ConvertFrom-Json) 18
|
||||
$point=BallPoint $board 's18-b04'
|
||||
if(WorldSafe @($point[0],($point[1]-120))){throw 'Actual unsafe-endpoint fixture is unexpectedly safe'}
|
||||
foreach($kind in @('unchanged','disappeared','wrong-free','counts-changed','collection-changed','camera-changed')){
|
||||
$raw=Get-Content -LiteralPath $BlockedFixture -Raw|ConvertFrom-Json
|
||||
switch($kind){
|
||||
'disappeared'{$raw.puzzleStates=@($raw.puzzleStates|Where-Object stageId -ne 18)}
|
||||
'wrong-free'{$raw.puzzleStates=@($raw.puzzleStates|Where-Object stageId -ne 18);$raw.completedStageIds+=18}
|
||||
'counts-changed'{(Board $raw 18).countedBallIds+= 's18-b04'}
|
||||
'collection-changed'{(Board $raw 18).collectedBallIds+= 's18-b04'}
|
||||
'camera-changed'{(Board $raw 18).camera.distance+=.1}
|
||||
}
|
||||
$script:liftRequests.Clear();$rejected=$false;$value=$null
|
||||
try{$value=LiftMergeRoute 18 "unsafe-route-$kind" $board}catch{$rejected=$true}
|
||||
$passed=if($kind-eq'unchanged'){!$rejected-and(Same $value.State $raw)}else{$rejected}
|
||||
Record 'unsafe-detour-requires-exact-present-board-without-touch' ($passed-and$script:liftRequests.Count-eq0) $kind
|
||||
}
|
||||
}
|
||||
}catch{Record 'production-function-evaluation' $false $_.Exception.Message}
|
||||
$passed=$checks.Count-gt0-and@($checks|Where-Object passed -ne $true).Count-eq0
|
||||
@{passed=$passed;scope='Offline actual failed save replay and native-input/assertion negative controls; no ADB, no Unity, no native success claim';fixture=$Fixture;fixtureSha256=(Get-FileHash -LiteralPath $Fixture).Hash;commonSource=$CommonSource;commonSha256=(Get-FileHash -LiteralPath $CommonSource).Hash;progressionSource=$ProgressionSource;progressionSha256=(Get-FileHash -LiteralPath $ProgressionSource).Hash;checks=$checks.ToArray()}|ConvertTo-Json -Depth 12|Set-Content -LiteralPath (Join-Path $EvidenceDir 'live-drag-result.json')
|
||||
@{passed=$passed;scope='Offline actual failed save replay and native-input/assertion negative controls; no ADB, no Unity, no native success claim';fixture=$Fixture;fixtureSha256=(Get-FileHash -LiteralPath $Fixture).Hash;routeFixtures=@(@($BlockedFixture,$LiftBefore,$LiftAfter,$FineFixture)|Where-Object {$_}|ForEach-Object {@{path=$_;sha256=(Get-FileHash -LiteralPath $_).Hash}});commonSource=$CommonSource;commonSha256=(Get-FileHash -LiteralPath $CommonSource).Hash;progressionSource=$ProgressionSource;progressionSha256=(Get-FileHash -LiteralPath $ProgressionSource).Hash;checks=$checks.ToArray()}|ConvertTo-Json -Depth 12|Set-Content -LiteralPath (Join-Path $EvidenceDir 'live-drag-result.json')
|
||||
if(!$passed){exit 1};exit 0
|
||||
|
||||
Reference in New Issue
Block a user