Keep native merge depth windows by reframing unsafe endpoints

This commit is contained in:
NINI
2026-10-11 01:11:17 +09:00
parent bb32fdae9d
commit 71bf8fab57
2 changed files with 91 additions and 1 deletions
+11 -1
View File
@@ -25,14 +25,19 @@ function Orbit([string]$Name,$CurrentBoard,[switch]$Pitch,[int]$YawPixels=120,[i
throw 'No safe blank orbit start in the actual board view.' throw 'No safe blank orbit start in the actual board view.'
} }
function ZoomOut([int]$Stage,$CurrentBoard) { function ZoomOut([int]$Stage,$CurrentBoard) {
$before=Save "stage-$Stage-zoom-out-before"
Check "stage-$Stage-current-pinch-projection" (Same $CurrentBoard (Board $before $Stage)) 'The pinch uses the exact current board and manual pose'
$helper=Join-Path $PSScriptRoot 'send-composition-touch.ps1' $helper=Join-Path $PSScriptRoot 'send-composition-touch.ps1'
foreach($contacts in @(@(30,400,999,1024),@(30,400,999,0),@(900,350,1,1024,1500,350,2,1024),@(1050,350,1,1024,1350,350,2,1024),@(1050,350,1,0,1350,350,2,0))){ foreach($contacts in @(@(30,400,999,1024),@(30,400,999,0),@(900,350,1,1024,1500,350,2,1024),@(1050,350,1,1024,1350,350,2,1024),@(1050,350,1,0,1350,350,2,0))){
& pwsh -NoProfile -Command "& '$helper' -Contacts $($contacts-join',')" & pwsh -NoProfile -Command "& '$helper' -Contacts $($contacts-join',')"
$code=$LASTEXITCODE;$commands.Add(@{command='native sendTouch pinch-out';contacts=$contacts;exitCode=$code;utc=[DateTime]::UtcNow.ToString('o')});if($code-ne0){throw 'Native pinch failed'};Start-Sleep -Milliseconds 120 $code=$LASTEXITCODE;$commands.Add(@{command='native sendTouch pinch-out';contacts=$contacts;exitCode=$code;utc=[DateTime]::UtcNow.ToString('o')});if($code-ne0){throw 'Native pinch failed'};Start-Sleep -Milliseconds 120
} }
Step "stage-$Stage-zoom-out" @() 300 Step "stage-$Stage-zoom-out" @() 300
$after=Board (Save "stage-$Stage-zoom-out-state") $Stage $state=Save "stage-$Stage-zoom-out-state";$after=Board $state $Stage
Check "stage-$Stage-pinch-preserves-board" ((Same $CurrentBoard.balls $after.balls)-and(Same $CurrentBoard.groups $after.groups)-and(Same $CurrentBoard.countedBallIds $after.countedBallIds)-and$after.camera.distance-gt$CurrentBoard.camera.distance) 'Actual two-finger zoom-out; no ball, membership or count edits' Check "stage-$Stage-pinch-preserves-board" ((Same $CurrentBoard.balls $after.balls)-and(Same $CurrentBoard.groups $after.groups)-and(Same $CurrentBoard.countedBallIds $after.countedBallIds)-and$after.camera.distance-gt$CurrentBoard.camera.distance) 'Actual two-finger zoom-out; no ball, membership or count edits'
$expected=$state|ConvertTo-Json -Depth 20|ConvertFrom-Json
(Board $expected $Stage).camera=$CurrentBoard.camera
Check "stage-$Stage-pinch-preserves-whole-save" (Same $before $expected) 'Only this board manual camera may change; every independent record remains exact'
return $after return $after
} }
function RevealBall([int]$Stage,[string]$Id,[string]$Name) { function RevealBall([int]$Stage,[string]$Id,[string]$Name) {
@@ -99,6 +104,11 @@ function MergeReal([int]$Stage,[string]$Name,[int]$SourceCount=0,[int]$TargetCou
$targetPoint=BallPoint $board $candidate.tid $targetPoint=BallPoint $board $candidate.tid
if(WorldSafe @(($targetPoint[0]+$offset),$targetPoint[1])){$pair=$candidate;break} if(WorldSafe @(($targetPoint[0]+$offset),$targetPoint[1])){$pair=$candidate;break}
} }
if(!$pair-and$projection-eq0-and$board.camera.distance-lt15){
# Keep a visited contact depth plane when its endpoint is outside the UI-safe view.
$needsFrame=@($pairs|Where-Object {$point=BallPoint $board $_.tid;$_.depth-lt.9-and(BallClear $board $_.sid)-and!(WorldSafe @(($point[0]+$offset),$point[1]))}).Count-gt0
if($needsFrame){$board=ZoomOut $Stage $board;continue}
}
if($pair-or$attempt-ne0-or$projection-ne0){break} if($pair-or$attempt-ne0-or$projection-ne0){break}
$routed=LiftMergeRoute $Stage "$Name-upward-route" $board $SourceCount $TargetCount $routed=LiftMergeRoute $Stage "$Name-upward-route" $board $SourceCount $TargetCount
if(!$routed){break} if(!$routed){break}
@@ -0,0 +1,80 @@
param([Parameter(Mandatory)][string]$EvidenceDir,[Parameter(Mandatory)][string]$FixtureDir,[string]$ProgressionSource=(Join-Path $PSScriptRoot 'exercise-final-progression.ps1'))
$ErrorActionPreference='Stop'
New-Item -ItemType Directory $EvidenceDir -Force|Out-Null
$checks=[Collections.Generic.List[object]]::new();$script:inputs=[Collections.Generic.List[object]]::new()
function Record($Id,$Passed,$Actual){$checks.Add(@{id=$Id;passed=[bool]$Passed;actual=$Actual})}
function Load($Path,$Name){
$ast=[Management.Automation.Language.Parser]::ParseFile($Path,[ref]$null,[ref]$null)
$f=$ast.Find({param($n)$n-is[Management.Automation.Language.FunctionDefinitionAst]-and$n.Name-eq$Name},$true)
if(!$f){throw "Missing function $Name"}
$expected=$f.Extent.Text.Replace("function $Name","function global:$Name").Replace('$PSScriptRoot',"'$PSScriptRoot'")
. ([scriptblock]::Create($expected))
Record "$Name-production-body" ((Get-Command $Name).ScriptBlock.Ast.Extent.Text-ceq$expected) $Path
}
function Clone($Value){return $Value|ConvertTo-Json -Depth 30|ConvertFrom-Json}
function Adb {return 'Physical size: 1080x2400'}
function Check($Id,$Passed,$Actual){if(!$Passed){throw "$Id : $Actual"}}
function Start-Sleep {}
# External input leaves are inert. Geometry below is counterfactual framing, never native proof.
function pwsh {$script:inputs.Add('pinch-leaf');$global:LASTEXITCODE=0}
function NativePointer {}
function Step {}
function global:LiftMergeRoute {return $null}
$commands=[Collections.Generic.List[object]]::new()
$script:current=$null;$script:zoomed=$false;$script:badZoom=$false;$script:caseState=$null
function Save([string]$Name){
if($Name-match'-before-(\d+)$'){$script:current=if($script:caseState){Clone $script:caseState}else{Get-Content (Join-Path $FixtureDir "stage-16-merge-4-before-$($Matches[1]).json") -Raw|ConvertFrom-Json};$script:zoomed=$false}
if($Name-match'zoom-out-state'){
$script:current=Clone $script:current
(Board $script:current 16).camera.distance=15
if($script:badZoom){$script:current.completedStageIds+=16}
$script:zoomed=$true
}
return Clone $script:current
}
function global:NativeMoveAndHold($Name,$Board,$Id,$End){
if(!(BallClear $Board $Id)-or!(WorldSafe $End)){throw 'unsafe-counterfactual-input'}
$script:inputs.Add(@{id=$Id;end=$End;distance=$Board.camera.distance;zoomed=$script:zoomed})
throw 'safe-framed-drop-selected'
}
try{
foreach($n in @('BallPoint','WorldSafe','BallClear','Same')){Load (Join-Path $PSScriptRoot 'final-common.ps1') $n}
foreach($n in @('Board','Orbit','ZoomOut','MergeReal')){Load $ProgressionSource $n}
$errorText=$null;try{MergeReal 16 'offline-merge16'|Out-Null}catch{$errorText=$_.Exception.Message}
$drops=@($script:inputs|Where-Object {$_-isnot[string]})
Record 'actual-depth-window-reframes-and-reprojects-within-original-bound' ($errorText-eq'safe-framed-drop-selected'-and$drops.Count-eq1-and$drops[0].zoomed-and$drops[0].distance-eq15) @{error=$errorText;inputs=$script:inputs.ToArray();scope='Offline decision/input-leaf replay only; no merge success inferred'}
# A native pinch must not mask an independent completion record change.
$script:badZoom=$true;$script:inputs.Clear();$errorText=$null
try{MergeReal 16 'bad-zoom'|Out-Null}catch{$errorText=$_.Exception.Message}
Record 'pinch-independent-record-corruption-rejected-before-ball-input' ($errorText-match'pinch-preserves-whole-save'-and@($script:inputs|Where-Object {$_-isnot[string]}).Count-eq0) $errorText
$script:badZoom=$false
foreach($kind in @('already-safe-pair','no-eligible-depth','no-source-first-hit','maximum-distance','no-eligible-source-size','offset-only-unsafe')){
$index=if($kind-eq'no-eligible-depth'){0}elseif($kind-eq'maximum-distance'){11}else{2}
$script:caseState=Get-Content (Join-Path $FixtureDir "stage-16-merge-4-before-$index.json") -Raw|ConvertFrom-Json
$b=Board $script:caseState 16;$sourceCount=1;$targetCount=5
switch($kind){
'already-safe-pair'{$b.camera.distance=15}
'no-source-first-hit'{foreach($ball in $b.balls){$ball.position=Clone $b.balls[0].position}}
'maximum-distance'{foreach($ball in $b.balls){$ball.position.x*=15/8;$ball.position.y=1.25+($ball.position.y-1.25)*15/8;$ball.position.z*=15/8};$b.camera.distance=15}
'no-eligible-source-size'{$sourceCount=2}
'offset-only-unsafe'{
$b.camera.rotation=[pscustomobject]@{x=0;y=0;z=0;w=1}
foreach($ball in $b.balls){$ball.position=[pscustomobject]@{x=-8;y=2.5;z=0}}
@($b.balls|Where-Object id -eq 's16-b04')[0].position=[pscustomobject]@{x=0;y=1.25;z=0}
@($b.balls|Where-Object id -eq 's16-b05')[0].position=[pscustomobject]@{x=-5.94;y=2.5;z=0}
$point=BallPoint $b 's16-b05'
Record 'offset-control-center-safe-endpoint-unsafe' ((WorldSafe $point)-and!(WorldSafe @(($point[0]-35),$point[1]))) $point
}
}
$script:inputs.Clear();$errorText=$null;try{MergeReal 16 "control-$kind" $sourceCount $targetCount|Out-Null}catch{$errorText=$_.Exception.Message}
$pinches=@($script:inputs|Where-Object {$_-is[string]}).Count
$drops=@($script:inputs|Where-Object {$_-isnot[string]})
if($kind-eq'offset-only-unsafe'){$ok=$pinches-eq5-and$errorText-eq'safe-framed-drop-selected'-and$drops.Count-eq1-and$drops[0].zoomed}
elseif($kind-eq'already-safe-pair'){$ok=$pinches-eq0-and$errorText-eq'safe-framed-drop-selected'-and$drops.Count-eq1-and!$drops[0].zoomed}
else{$ok=$pinches-eq0-and$drops.Count-eq0-and$errorText-match'Native merge bound reached'}
Record "branch-control-$kind" $ok @{pinchLeaves=$pinches;drops=$drops;error=$errorText;scope='Explicitly counterfactual boundary control'}
}
}catch{Record 'evaluation' $false $_.Exception.Message}
$passed=$checks.Count-gt0-and@($checks|Where-Object passed -ne $true).Count-eq0
@{passed=$passed;scope='Offline frozen failed execution replay; mocked native leaves and explicitly counterfactual distance15; not Android completion';progressionSha256=(Get-FileHash $ProgressionSource).Hash;fixtures=@(0..15|ForEach-Object {$p=Join-Path $FixtureDir "stage-16-merge-4-before-$_.json";@{path=$p;sha256=(Get-FileHash $p).Hash}});checks=$checks.ToArray()}|ConvertTo-Json -Depth 12|Set-Content (Join-Path $EvidenceDir 'merge-framing-result.json')
if(!$passed){exit 1};exit 0