docs+tools: a mission freeze that made an experiment lie, and the counter is not a class head-count
Three things from one Stage 02 run. The address recurs a third time: HUD=4 RAM=4 at 0xbdb59668, so 3 of the 5 runs measured put the counter exactly there. The counter is NOT a live class head-count. With the counter at 4 the typed entity list was 8 attackers, 7 friendly Delta Sabers, 7 turrets and the player - no class has 4 members and no pair of them sums to 4. That sharpens the corpus's existing "012 against 118 live ADAN" note from "not the hostile count" to "not the count of any class this enumeration can see". The flag experiment itself proves nothing, and why is the useful part. It found 20 offsets where exactly 4 of 23 entities agree, then reported "the counter never moved" for 600 s. The guest had stopped advancing ten seconds into flight: pilot.py logged 724 s of identical speed/yaw/pitch, and two screenshots six seconds apart were byte-identical, max delta 0 over 863325 pixels - while screen_id said "flight", the emulator burned 212% CPU and every liveness check passed. So that was a fact about a dead world. Withdrawn along with it: the claim in ob_session.sh that the counter climbs on its own in the first minutes, which one advancing run supports and this one cannot. frozen.py makes it a single call, checked in both directions (0 on the frozen pair, 254 on two frames of a live run), and ob_hunt/ob_flag now say GUEST FROZEN rather than waiting out their timeouts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
This commit is contained in:
70
tools/re-capture/frozen.py
Executable file
70
tools/re-capture/frozen.py
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Is the guest still ANIMATING, or has the mission frozen?
|
||||
|
||||
Measured 2026-08-23: a Stage 02 run entered flight, drew a correct HUD, and then
|
||||
stopped advancing about ten seconds later. `screen_id.py` still said `flight`,
|
||||
the emulator still burned 212 % CPU, `pilot.py` still logged 5 900 samples, and
|
||||
every one of them carried the same speed, yaw and pitch — 724 s of identical
|
||||
state. Two screenshots six seconds apart were **byte-identical**, max difference
|
||||
0 over 863 325 pixels.
|
||||
|
||||
That is worth its own test because of what it costs when missed: an experiment
|
||||
that waits for `REMAINING OB` to change will wait out its whole timeout and then
|
||||
report "the counter never moved", which reads as a fact about the game and is
|
||||
really a fact about a dead world. Nothing else in the toolkit notices — the
|
||||
screen classifier, the liveness check and the CPU are all happy.
|
||||
|
||||
A frozen frame is EXACTLY identical, not merely similar: this is a stopped
|
||||
simulation, not a still scene, so no tolerance is needed and none is used. The
|
||||
one thing that must be excluded is a screenshot that failed.
|
||||
|
||||
Usage: frozen.py [gap_s] -> prints frozen|animating, exit 0 if FROZEN
|
||||
frozen.py --pair <a.png> <b.png> -> same test on two saved frames, so the
|
||||
NEGATIVE side can be checked without
|
||||
a live game to be un-frozen in
|
||||
"""
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
from PIL import Image, ImageChops
|
||||
|
||||
|
||||
def grab(path):
|
||||
subprocess.run(["screenshot", path], stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL)
|
||||
return Image.open(path).convert("RGB")
|
||||
|
||||
|
||||
def frozen(gap=6.0):
|
||||
a = grab("/tmp/frz-a.png")
|
||||
time.sleep(gap)
|
||||
b = grab("/tmp/frz-b.png")
|
||||
if a.size != b.size:
|
||||
return None, -1
|
||||
bbox = ImageChops.difference(a, b).getbbox()
|
||||
px = list(ImageChops.difference(a.convert("L"), b.convert("L")).getdata())
|
||||
return (bbox is None), max(px)
|
||||
|
||||
|
||||
def compare(pa, pb):
|
||||
a, b = Image.open(pa).convert("RGB"), Image.open(pb).convert("RGB")
|
||||
if a.size != b.size:
|
||||
return None, -1
|
||||
px = list(ImageChops.difference(a.convert("L"), b.convert("L")).getdata())
|
||||
return (ImageChops.difference(a, b).getbbox() is None), max(px)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "--pair":
|
||||
f, mx = compare(sys.argv[2], sys.argv[3])
|
||||
gap = "pair"
|
||||
else:
|
||||
gap = float(sys.argv[1]) if len(sys.argv) > 1 else 6.0
|
||||
f, mx = frozen(gap)
|
||||
if f is None:
|
||||
print("unknown (screenshot failed)")
|
||||
sys.exit(2)
|
||||
suffix = gap if gap == "pair" else f"{gap}s"
|
||||
print(f"{'frozen' if f else 'animating'} max_pixel_delta={mx} gap={suffix}")
|
||||
sys.exit(0 if f else 1)
|
||||
Reference in New Issue
Block a user