Files
Syplheed-Reborn/tools/re-capture/autopilot.py
Claude (auto-RE) 07eff85819 re: in-flight HUD — independent confirmation of the ammo mapping
The tutorials need no save and are ~1 min from a cold boot, so they are the
cheapest route into a live flight scene. The HUD prints the equipped weapons as
NOSE BM 06000 / MAIN MPM 00300 — matching wep_01's LoadingCount 6000 and wep_02's
300, with the weapons identified from the HANGAR loadout rather than from the
ammo number. That makes it a genuinely independent confirmation of
Ammo Capacity == LoadingCount, from a different renderer in a different mode.

Also inventories the HUD elements (heat gauges, shield/armor pools, target armor
gauge, per-weapon RANGE markers) and notes that the RANGE gauge draws each
weapon's MaximumRange on a scale — a route to a real number where the Arsenal
panel only gives a letter class.

Adds autopilot.py (waypoint-arrow chaser). It detects reliably but oscillates;
recorded as such rather than as a working tool.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 18:50:46 +00:00

96 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""Fly the Delta Saber toward the tutorial waypoint by chasing the yellow
off-screen direction arrow.
Detection: no PIL/numpy in the box, so the frame comes from `convert ... txt:-`.
Two things make it fast AND correct:
* `-sample` (point sampling, not `-resize`/`-scale` averaging) — a 1-px-thin
glyph keeps its true colour, so the exact colour predicate still fires while
the dump shrinks ~9x (3.5s -> 0.65s).
* shape, not just colour — the instruction-frame bars and the throttle marker
are the same dim yellow, so the arrow is the largest blob that is roughly as
tall as it is wide, with the fixed throttle marker blacklisted by position.
A ~1.1 s control loop is fast enough to converge; the earlier ~6 s loop was not.
"""
import re, subprocess, sys, time
X0, Y0, W, H = 80, 60, 820, 640 # flight view (the arrow also rides the bottom edge)
K = 3.03 # 1/0.33 sample factor
CX, CY = 640, 405 # crosshair
THROTTLE = (382, 456) # fixed yellow decoy on the speed gauge
PX = re.compile(r"^(\d+),(\d+):.*?srgb\((\d+),(\d+),(\d+)\)")
def pad(*a):
subprocess.run(["/opt/sylph/vgamepad.py", *a], check=False)
def arrow(png="/tmp/ap.png"):
subprocess.run(["/opt/sylph/screenshot.sh", png], check=False,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
out = subprocess.run(["convert", png, "-crop", f"{W}x{H}+{X0}+{Y0}", "+repage",
"-sample", "33%", "txt:-"], capture_output=True, text=True).stdout
pts = set()
for l in out.splitlines()[1:]:
m = PX.match(l)
if not m:
continue
x, y, r, g, b = (int(v) for v in m.groups())
if r >= 60 and g >= 48 and b <= 0.35 * g and (r - g) <= 0.45 * r:
pts.add((x, y))
seen, best = set(), None
for p in pts:
if p in seen:
continue
st, c = [p], []
seen.add(p)
while st:
x, y = st.pop(); c.append((x, y))
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
q = (x + dx, y + dy)
if q in pts and q not in seen:
seen.add(q); st.append(q)
xs = [q[0] for q in c]; ys = [q[1] for q in c]
cx, cy = X0 + sum(xs) / len(c) * K, Y0 + sum(ys) / len(c) * K
if abs(cx - THROTTLE[0]) < 30 and abs(cy - THROTTLE[1]) < 30:
continue
if len(c) < 8:
continue
if best is None or len(c) > best[2]:
best = (cx, cy, len(c))
return best
def main():
steps = int(sys.argv[1]) if len(sys.argv) > 1 else 25
centred = 0
for i in range(steps):
a = arrow()
if a is None:
print(f"{i:2d}: no arrow -> boost (target ahead)")
pad("trig", "RT", "0.55"); time.sleep(1.2); pad("trig", "RT", "0")
centred += 1
if centred >= 6:
print(" arrival likely — stopping"); return 0
continue
x, y, n = a
dx, dy = x - CX, y - CY
if abs(dx) < 70 and abs(dy) < 70:
centred += 1
print(f"{i:2d}: arrow ({x:.0f},{y:.0f}) centred -> boost")
pad("trig", "RT", "0.55"); time.sleep(1.2); pad("trig", "RT", "0")
continue
centred = 0
lx = max(-1.0, min(1.0, dx / 200))
ly = max(-1.0, min(1.0, dy / 160))
print(f"{i:2d}: arrow ({x:.0f},{y:.0f}) n={n} -> LX {lx:+.2f} LY {ly:+.2f}")
pad("axis", "LX", f"{lx:.2f}"); pad("axis", "LY", f"{ly:.2f}")
time.sleep(0.45)
pad("axis", "LX", "0"); pad("axis", "LY", "0")
print("steps exhausted")
return 1
sys.exit(main())