diff --git a/tools/android/exercise-playground.ps1 b/tools/android/exercise-playground.ps1 index e0f3fd7..5f0c4d6 100644 --- a/tools/android/exercise-playground.ps1 +++ b/tools/android/exercise-playground.ps1 @@ -175,7 +175,7 @@ function ApproachDepth([string]$Name,$Source){ $sourcePoint=@($friends|Sort-Object {[Math]::Pow($_.x-$pointer[0],2)+[Math]::Pow($_.y-$pointer[1],2)})[0] $targetPoint=@($friends|Where-Object {$_-ne$sourcePoint})[0] $dx=$targetPoint.x-$sourcePoint.x;$dy=$targetPoint.y-$sourcePoint.y;$length=[Math]::Sqrt($dx*$dx+$dy*$dy) - if($length-lt2){break};$scale=[Math]::Min(1,12/$length) + if($length-lt2){break};$scale=[Math]::Min(1.0,12.0/$length) $next=@([int][Math]::Round($pointer[0]+$dx*$scale),[int][Math]::Round($pointer[1]+$dy*$scale)) if(!(WorldSafe $next)){throw 'Live depth approach leaves UI-safe input region'} $pointer=$next;NativeTouch @($pointer[0],$pointer[1],73,1024) diff --git a/tools/android/test-playground-contracts.ps1 b/tools/android/test-playground-contracts.ps1 index 4c3886e..4129b32 100644 --- a/tools/android/test-playground-contracts.ps1 +++ b/tools/android/test-playground-contracts.ps1 @@ -167,6 +167,54 @@ exit ([int]$env:PLAYGROUND_CONTROL_TOUCH_EXIT) $point=@([int]1096,[int]609);$pointer=$null;$pointerError=$null try{. ([scriptblock]::Create($pointerAssignment.Extent.Text))}catch{$pointerError=$_.Exception.Message} Probe 'actual-deep-contact-pointer-exact-native-point' ($null-eq$pointerError-and$pointer-is[array]-and$pointer.Count-eq2-and$pointer[0]-is[int]-and$pointer[1]-is[int]-and$pointer[0]-eq1116-and$pointer[1]-eq609) @{statement=$pointerAssignment.Extent.Text;actualPoint=$point;pointer=$pointer;error=$pointerError;scope='Actual ApproachDepth assignment with observed native first-hit point; no native input'} + $commonAst=[Management.Automation.Language.Parser]::ParseFile((Join-Path $PSScriptRoot 'final-common.ps1'),[ref]$null,[ref]$null) + $worldSafe=$commonAst.Find({param($node)$node-is[Management.Automation.Language.FunctionDefinitionAst]-and$node.Name-eq'WorldSafe'},$true) + function RunApproachControl($Case,[string]$Mutation='valid'){ + & { + param($Definition,$SafeDefinition,$Source,$Case,$Mutation) + . ([scriptblock]::Create($Definition));. ([scriptblock]::Create($SafeDefinition)) + $commands=[Collections.Generic.List[object]]::new();$contactLog=[Collections.Generic.List[object]]::new();$vision=@{calls=0} + $fixtures=@{stages=@(@{stage=1;min=@{x=-8;y=0;z=-6};max=@{x=8;y=6;z=6}})} + function FixtureState($Board,[string]$Type,[int]$Stage){$state=Clone $floatProduction;$state.latestFreePlay=Clone $Board;return $state} + function LoadFixture([string]$Name,$State,[string]$Type,[int]$Stage){return Clone $State} + function BallPoint($Board,[string]$Id){return @(1096,609)} + function NativeTouch([int[]]$Contacts){$contactLog.Add([object]$Contacts)} + function Start-Sleep([int]$Milliseconds){} + function Step([string]$Name,[string[]]$Arguments=@(),[int]$Delay=500){} + function BlueFriends([string]$Path){$vision.calls++;if($vision.calls-eq1){return @(@{x=$Case.sourceX;y=$Case.sourceY},@{x=$Case.targetX;y=$Case.targetY})};return @()} + $controlAfter=FixtureState $Source 'free' 1 + $controlAfter.latestFreePlay.balls[0].position=[pscustomobject]@{x=0;y=3;z=0};$controlAfter.latestFreePlay.balls[1].position=[pscustomobject]@{x=1;y=3;z=0} + $controlAfter.latestFreePlay.groups=@([pscustomobject]@{id='steering-control-merged';ballIds=@($Source.balls.id)}) + switch($Mutation){ + 'wrong-id'{$controlAfter.latestFreePlay.balls[0].id='wrong-id'} + 'wrong-membership'{$controlAfter.latestFreePlay.groups[0].ballIds=@($Source.balls[0].id)} + 'camera-one-ulp'{$controlAfter.latestFreePlay.camera.distance=[double][MathF]::BitIncrement([single]$controlAfter.latestFreePlay.camera.distance)} + 'outside-bounds'{$controlAfter.latestFreePlay.balls[0].position.x=100;$controlAfter.latestFreePlay.balls[1].position.x=101} + 'wrong-contact'{$controlAfter.latestFreePlay.balls[1].position.x=2} + } + function Save([string]$Name){return Clone $controlAfter} + $accepted=$false;$message=$null;try{ApproachDepth 'steering-control' $Source;$accepted=$true}catch{$message=$_.Exception.Message} + return @{accepted=$accepted;contacts=$contactLog.ToArray();observations=$vision.calls;error=$message} + } $approach.Extent.Text $worldSafe.Extent.Text $producer $Case $Mutation + } + $steeringCases=@( + @{name='actual-native-far';sourceX=1143.320388349515;sourceY=609.5533980582524;targetX=1384.984126984127;targetY=609.6190476190476;expected=@(1128,609)}, + @{name='mid-right';sourceX=1116;sourceY=609;targetX=1140;targetY=609;expected=@(1128,609)}, + @{name='far-left';sourceX=1116;sourceY=609;targetX=996;targetY=609;expected=@(1104,609)}, + @{name='far-up';sourceX=1116;sourceY=609;targetX=1116;targetY=489;expected=@(1116,597)}, + @{name='mid-down';sourceX=1116;sourceY=609;targetX=1116;targetY=633;expected=@(1116,621)}, + @{name='near-left';sourceX=1116;sourceY=609;targetX=1111;targetY=609;expected=@(1111,609)}, + @{name='near-diagonal';sourceX=1116;sourceY=609;targetX=1119;targetY=613;expected=@(1119,613)} + ) + foreach($case in $steeringCases){ + $run=RunApproachControl $case;$moves=@($run.contacts|Where-Object {$_[3]-eq1024});$next=if($moves.Count-eq3){$moves[2]}else{$null} + $correct=$run.accepted-and$run.observations-eq2-and$moves.Count-eq3-and$next[0]-eq$case.expected[0]-and$next[1]-eq$case.expected[1]-and([Math]::Pow($next[0]-1116,2)+[Math]::Pow($next[1]-609,2)-le144) + Probe "actual-approach-depth-steering-$($case.name)" $correct @{case=$case;run=$run;scope='Actual full ApproachDepth AST with PNG/touch/save transport doubles; expected exact direction and maximum12px step, no native verdict'} + } + foreach($mutation in @('wrong-id','wrong-membership','camera-one-ulp','outside-bounds','wrong-contact')){ + $run=RunApproachControl $steeringCases[0] $mutation + Probe "actual-approach-depth-contact-rejects-$mutation" (!$run.accepted-and$run.error-like'*actual-id-contact-merge*') @{run=$run;scope='Actual full ApproachDepth real3D contact Check rejects invalid saved result; transport replay only'} + } $reentry=$gestureAst.Find({param($node)$node-is[Management.Automation.Language.FunctionDefinitionAst]-and$node.Name-eq'SameProgress'},$true) if($reentry){. ([scriptblock]::Create($reentry.Extent.Text))} Probe 'actual-reentry-comparator-present' ($null-ne$reentry) 'Only outer puzzleStates order may be canonicalized; Same remains strict'