Files
EscapistsReintroduced/tools/prison_approach_proof.py
T

334 lines
19 KiB
Python

"""Bounded approach-only policy; no native library, display, process, or action calls.
Adapter contract (all records must be parsed from the same pinned console/nonce):
* observe() freshly joins INPUT/LOOK/GATE by sample+real_ms, validates the native
BASIS and absence of cancellation, and returns an ordered nonempty batch of
snapshots (all new consecutive samples, or repeat latest). Each has sample, real_ms,
received_monotonic, origin, direction, body_forward, input, forward, velocity,
inventory, phase, door_state, door_control, and geometry.
* geometry: nonce, basis, prison, gate, gate_forward, context, radius. Coordinates
are native GEOMETRY values; binding is the native observer's pinned references,
not a fabricated entity ID. This policy supports only the authored axis-aligned
12m enclosure; it derives an interior target from the observed handle.
* calibration is independently accepted same-run tiny look evidence projected to
nonce, completed, pixels, baseline_direction, response_direction,
ready_real_ms, ready_monotonic, observer_start_real_ms.
* guard.check() is the existing full private ownership/focus guard. keyboard has
require_idle()/send('W', bool). mouse.move(dx,0,guard) must use the same guarded
connection and current/projected clipped pointer checks, explicitly permitting
the explicitly selected standard128px or coarse384px cap. Existing two-motion
helper remains unchanged; profile choice never changes the observation clocks.
* notify receives the existing press-pending/release-confirmed events, plus
approach-pending (absolute monotonic deadline)/approach-finished. The wiring
MUST hard-supervise that deadline and the unchanged 1.5-second key lease.
* key_state_busy is the existing explicit Keyboard KeyStateBusy exception class;
other failures are never retried as busy-state polls.
No adapter implementation or input is executed by importing this module. A pass
proves an interior approach only, never reach, action selection, unlock or escape.
"""
import copy
import math
class Refused(ValueError):
pass
def require(value, reason):
if not value:
raise Refused(reason)
def number(value):
require(type(value) in (int, float) and math.isfinite(value), 'Nonfinite or invalid numeric observation')
return float(value)
def vector(value):
require(type(value) in (list, tuple) and len(value) == 3, 'Invalid vector observation')
result = tuple(number(v) for v in value)
require(all(abs(v) <= 10000000 for v in result), 'Unbounded native vector')
return result
def horizontal(a, b):
return math.hypot(a[0]-b[0], a[2]-b[2])
def length(value):
return math.sqrt(sum(v*v for v in value))
def yaw(direction):
d = vector(direction)
require(.95 <= length(d) <= 1.05 and math.hypot(d[0], d[2]) > .1, 'Invalid native direction')
return math.degrees(math.atan2(d[0], d[2]))
def delta(target, actual):
return (target-actual+180) % 360-180
def target_for(geometry):
require(type(geometry) is dict and set(geometry) == {'nonce', 'basis', 'prison', 'gate', 'gate_forward', 'context', 'radius'}, 'Incomplete native gate geometry')
require(type(geometry['nonce']) is str and 1 <= len(geometry['nonce']) <= 32 and geometry['nonce'].isascii() and geometry['nonce'].isalnum(), 'Invalid geometry nonce')
require(geometry['basis'] == 'registered-prison-lock-native-interaction', 'Missing pinned native gate basis')
prison, gate, context = (vector(geometry[k]) for k in ('prison', 'gate', 'context'))
facing = vector(geometry['gate_forward']); yaw(facing)
require(0 < number(geometry['radius']) <= 10, 'Invalid native context radius')
dx, dz = prison[0]-gate[0], prison[2]-gate[2]
distance = math.hypot(dx, dz)
require(5.75 <= distance <= 6.25 and min(abs(dx), abs(dz)) <= .1, 'Unsupported measured prison gate placement')
inward = (dx/distance, 0., dz/distance)
require(abs(facing[0]*inward[0]+facing[2]*inward[2]) >= .99, 'Gate orientation differs from authored enclosure')
require(horizontal(context, gate) <= 4.5, 'Context is not near original gate')
target = (context[0]+1.5*inward[0], prison[1], context[2]+1.5*inward[2])
require(inside(target, prison), 'Derived approach is outside safe prison interior')
return target, prison
def inside(position, center):
return abs(position[0]-center[0]) <= 5.25 and abs(position[2]-center[2]) <= 5.25
def perform_approach(guard, keyboard, mouse, observe, geometry, calibration, report, *, clock, sleep, notify, key_state_busy, profile="standard"):
"""Run one bounded route through explicit adapters; retain partial failure evidence."""
require(profile in ('standard', 'coarse') and type(profile) is str, 'Unknown approach profile')
max_pixels, max_angle, desired_angle, max_pulses = (384, 35, 25, 16) if profile == 'coarse' else (128, 15, 10, 12)
require('approach_proof' not in report, 'Approach attempt already exists')
pinned = copy.deepcopy(geometry)
target, center = target_for(pinned)
require(calibration.get('completed') is True and calibration.get('nonce') == pinned['nonce'] and calibration.get('pixels') == 24, 'Missing accepted same-run look calibration')
gain = delta(yaw(calibration['response_direction']), yaw(calibration['baseline_direction']))/24
require(.01 <= abs(gain) <= .5, 'Measured calibration gain outside bounded proposal range')
ready_at = number(calibration['ready_monotonic']); ready_real = number(calibration['ready_real_ms'])
native_start = number(calibration['observer_start_real_ms'])
require(0 <= native_start <= ready_real and 2000 <= ready_real-native_start < 45000, 'Invalid original native observation window')
require(0 <= ready_at <= clock(), 'Invalid original READY monotonic time')
deadline = ready_at+35
require(clock() < deadline, 'Insufficient original approach time')
state = {'profile': profile, 'requested': True, 'completed': False, 'nonce': pinned['nonce'], 'geometry': pinned,
'target': list(target), 'deadline_monotonic': deadline, 'started_monotonic': clock(),
'finished_monotonic': None, 'motions': [], 'events': [], 'stops': [],
'samples': [], 'cumulative_horizontal_m': 0., 'unconfirmed_held_key': None,
'paired_release_errors': [], 'release_timings': []}
report['approach_proof'] = state
previous = None; held = False; turn_count = 0; pixels = 0; pulse_count = 0
largest_stop = None; active_turn = None
def consume(s):
nonlocal previous
s = copy.deepcopy(s)
require(type(s['sample']) is int and 1 <= s['sample'] <= 450, 'Invalid native sample index')
real = number(s['real_ms']); received = number(s['received_monotonic'])
require(0 <= received <= clock(), 'Native receipt timestamp is invalid')
require(ready_real <= real and real-native_start < 45000 and real-ready_real < 35000, 'Original native lifetime exceeded')
require(s['geometry'] == pinned and s['phase'] == 1 and s['inventory'] is False, 'Original gate, prison phase or UI changed')
require(number(s['door_state']) == 0 and number(s['door_control']) == 0, 'Original gate is not closed and untouched')
for k in ('origin', 'direction', 'body_forward', 'input', 'velocity'):
s[k] = vector(s[k])
s['forward'] = number(s['forward']); yaw(s['direction']); yaw(s['body_forward'])
require(inside(s['origin'], center) and abs(s['origin'][1]-center[1]) <= 3, 'Body left bounded prison interior')
require(length(s['velocity']) <= 5, 'Native movement exceeds approach envelope')
require(abs(s['input'][0]) <= .01 and abs(s['input'][1]) <= .01 and -.01 <= s['input'][2] <= 1.01 and -.01 <= s['forward'] <= 1.01, 'Unexpected native movement input')
if active_turn is not None:
turn_yaw, turn_origin, turn_sign = active_turn
change = delta(yaw(s['direction']), turn_yaw)
require(abs(change) <= max_angle, 'Relative turn exceeded native angle bound')
require(horizontal(s['origin'], turn_origin) <= .05, 'Body translated during stationary heading')
if abs(change) >= .05:
require(change*turn_sign > 0, 'Native heading turned opposite to proposal')
if previous:
require(s['sample'] >= previous['sample'] and real >= previous['real_ms'], 'Native approach samples reversed')
if s['sample'] == previous['sample']:
require({k:v for k,v in s.items() if k != 'received_monotonic'} == {k:v for k,v in previous.items() if k != 'received_monotonic'}, 'Native sample changed without advancing')
else:
dt = real-previous['real_ms']
require(s['sample'] == previous['sample']+1 and 0 < dt <= 500, 'Native approach sampling gap')
distance = horizontal(s['origin'], previous['origin'])
require(distance <= .25+5*dt/1000, 'Native displacement exceeds continuous motion bound')
state['cumulative_horizontal_m'] += distance
require(state['cumulative_horizontal_m'] <= 6, 'Approach path budget exceeded')
if previous is None or s['sample'] != previous['sample']:
state['samples'].append(copy.deepcopy(s))
previous = s
return s
def read():
require(clock() < deadline, 'Original approach deadline exceeded')
guard.check()
batch = observe()
require(type(batch) is list and 1 <= len(batch) <= 450, 'Missing bounded native snapshot batch')
for item in batch:
s = consume(item)
require(clock() < deadline, 'Original approach deadline exceeded during observation')
require(0 <= clock()-s['received_monotonic'] <= .5, 'Native approach sample stale')
return s
def neutral(s):
return abs(s['forward']) <= .01 and length(s['input']) <= .01 and length(s['velocity']) <= .05
def wait_stopped(after, seconds, angle=None, moved_from=None, turn_sign=None):
end = min(deadline, clock()+seconds); first = None
while clock() < end:
s = read()
moved = moved_from is None or horizontal(s['origin'], moved_from) >= .05
if angle is not None:
change = delta(yaw(s['direction']), angle)
require(abs(change) <= max_angle, 'Relative turn exceeded native angle bound')
if abs(change) >= .05:
require(change*turn_sign > 0, 'Native heading turned opposite to proposal')
moved = moved and abs(change) >= .05
if s['sample'] > after and neutral(s) and moved:
if first is None:
first = s
elif s['real_ms']-first['real_ms'] >= 100:
if abs(delta(yaw(s['direction']), yaw(first['direction']))) <= .2 and horizontal(s['origin'], first['origin']) <= .02:
if angle is not None:
require(abs(delta(yaw(s['direction']), angle)) <= max_angle, 'Relative turn exceeded native angle bound')
require(clock() <= end, 'Settling exceeded original stage deadline')
return s
first = s
else:
first = None
sleep(.025)
raise Refused('Native full-stop or turn-settling acknowledgement missing')
def heading(s):
nonlocal turn_count, pixels, active_turn
end = min(deadline, clock()+20); growing = 0; flips = 0; old_sign = None
while True:
require(neutral(s), 'Heading correction requires stationary body')
wanted = math.degrees(math.atan2(target[0]-s['origin'][0], target[2]-s['origin'][2]))
error = delta(wanted, yaw(s['direction']))
if abs(error) <= 3:
return s
require(clock() < end and turn_count < 16, 'Heading request budget exceeded')
desired = max(-desired_angle, min(desired_angle, .75*error))
dx = int(round(desired/gain)); dx = max(-max_pixels, min(max_pixels, dx))
if dx == 0: dx = 1 if desired/gain > 0 else -1
require(pixels+abs(dx) <= 2048, 'Relative pixel budget exceeded')
before = read(); require(neutral(before), 'Body moved before turn')
keyboard.require_idle()
active_turn = (yaw(before['direction']), before['origin'], dx*gain)
mouse.move(dx, 0, guard)
turn_count += 1; pixels += abs(dx)
state['motions'].append({'dx': dx, 'dy': 0, 'monotonic': clock(), 'after_sample': before['sample']})
after = wait_stopped(before['sample'], 1.5, yaw(before['direction']), turn_sign=dx*gain)
state['motions'][-1]['response_sample'] = after['sample']
active_turn = None
require(horizontal(after['origin'], before['origin']) <= .05, 'Body translated during stationary heading')
change = delta(yaw(after['direction']), yaw(before['direction']))
require(abs(change) >= .05 and change*dx*gain > 0, 'Native heading failed or turned opposite to proposal')
new_error = delta(wanted, yaw(after['direction']))
growing = growing+1 if abs(new_error) >= abs(error) else 0
sign = new_error > 0
if old_sign is not None and sign != old_sign: flips += 1
old_sign = sign
require(growing < 2 and flips <= 2, 'Native heading failed to converge')
s = after
def release(not_before=None, cleanup=False):
nonlocal held
# At most two checks per normal pulse and one finally release attempt.
# These are diagnostic times, never authority to skip a fresh guard.
timing = {'cleanup': cleanup, 'guards': [], 'up_attempted_monotonic': None,
'up_returned_monotonic': None}
state['release_timings'].append(timing)
def checked():
record = {'entered_monotonic': clock(), 'returned_monotonic': None}
timing['guards'].append(record)
try:
guard.check()
except Exception as error:
record['error'] = str(error)
raise
finally:
record['returned_monotonic'] = clock()
# A native context failure does not prevent an owned release; only the
# full live private display/process/focus proof authorizes that release.
# Use the initial minimum hold to perform that proof instead of adding
# an unconditional 50ms before its already substantial runtime.
checked()
remaining = 0. if not_before is None else not_before-clock()
if remaining > 0:
sleep(remaining)
checked() # waiting invalidates the preceding ownership proof
timing['up_attempted_monotonic'] = clock()
keyboard.send('W', False)
timing['up_returned_monotonic'] = clock()
state['events'].append({'key': 'W', 'down': False, 'monotonic': clock()})
end = clock()+.35
while True:
guard.check()
try:
keyboard.require_idle()
break
except ValueError as error:
# Only the injected existing busy-state subtype is retryable.
if not isinstance(error, key_state_busy) or clock() >= end:
raise
sleep(min(.025, end-clock()))
held = False; state['unconfirmed_held_key'] = None
notify({'kind': 'release-confirmed', 'key': 'W'})
notify({'kind': 'approach-pending', 'deadline_monotonic': deadline})
try:
keyboard.require_idle()
current = read(); current = wait_stopped(current['sample']-1, 1.5)
initial_distance = horizontal(current['origin'], target)
require(.75 < initial_distance <= 6, 'Initial approach distance outside route scope')
while horizontal(current['origin'], target) > .75:
current = heading(current)
require(pulse_count < max_pulses, 'Forward pulse budget exceeded')
remaining = horizontal(current['origin'], target)
reserve = (largest_stop if largest_stop is not None else 1.25)+.25
require(remaining > reserve, 'Insufficient distance for measured stopping reserve')
ang = math.radians(yaw(current['direction']))
projected = (current['origin'][0]+reserve*math.sin(ang), current['origin'][1], current['origin'][2]+reserve*math.cos(ang))
require(inside(projected, center), 'Stopping reserve leaves safe interior')
before = read(); require(neutral(before), 'Forward pulse requires full native stop')
remaining = horizontal(before['origin'], target)
wanted = math.degrees(math.atan2(target[0]-before['origin'][0], target[2]-before['origin'][2]))
require(abs(delta(wanted, yaw(before['direction']))) <= 3, 'Fresh heading changed before W')
require(remaining > reserve, 'Fresh position exhausted stopping reserve')
ang = math.radians(yaw(before['direction']))
projected = (before['origin'][0]+reserve*math.sin(ang), before['origin'][1], before['origin'][2]+reserve*math.cos(ang))
require(inside(projected, center), 'Fresh stopping reserve leaves safe interior')
keyboard.require_idle()
require(clock() < deadline, 'Original approach deadline exceeded before press')
held = True; state['unconfirmed_held_key'] = 'W'
notify({'kind': 'press-pending', 'key': 'W'})
keyboard.send('W', True); pressed = clock()
state['events'].append({'key': 'W', 'down': True, 'monotonic': pressed})
release(not_before=pressed+.05)
released = state['events'][-1]['monotonic']
require(released-pressed <= .4, 'Actual W hold exceeded 400ms approach bound')
pulse_count += 1
after = wait_stopped(before['sample'], 1.5, moved_from=before['origin'])
require(any(s['forward'] > .01 and s['input'][2] > .01 for s in state['samples'] if before['sample'] < s['sample'] <= after['sample']), 'Native W acknowledgement missing from pulse samples')
distance = horizontal(before['origin'], after['origin'])
progress = remaining-horizontal(after['origin'], target)
require(.05 <= distance <= 1.25 and progress >= .05, 'Forward motion missed, overshot or moved away from target')
require(abs(delta(yaw(after['direction']), yaw(before['direction']))) <= 5, 'Heading drifted during forward pulse')
largest_stop = max(largest_stop or 0, distance)
state['stops'].append({'before_sample': before['sample'], 'after_sample': after['sample'], 'distance': distance, 'progress': progress})
current = after
final = read()
require(neutral(final) and horizontal(final['origin'], target) <= .75, 'Final native body is not stopped at approach target')
state['final_sample'] = final['sample']
keyboard.require_idle()
require(clock() < deadline, 'Original approach deadline exceeded at completion')
state['completed'] = True
return state
except Exception as error:
state['error'] = str(error)
raise
finally:
if held:
try: release(cleanup=True)
except Exception as error: state['paired_release_errors'].append(str(error))
state['finished_monotonic'] = clock()
notify({'kind': 'approach-finished', 'successful': state['completed']})