From 00052b0117f10c5cedaf6341dc87d60c7a6e9422 Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Thu, 27 Aug 2026 03:27:35 +0000 Subject: [PATCH] tools: add SYLPH_WEAKEST target scoring; record that assert_stage != in flight pilot.py gains SYLPH_WEAKEST=1, which scales a target's score by its remaining hull (pos+0x154) so the pilot finishes what is already hurt instead of re-engaging whatever is nearest. Motivated by b69cc23: over ~8 minutes the pilot damaged 14 of 16 e010 attackers (hulls 360..500) and killed none, because 500 HP spread across a squadron kills nobody. STATUS: the flag is implemented and its targeting works -- 3105 of 3105 target samples selected e010 -- but it is UNVERIFIED in combat, because the run it was written for was lost. That loss is the second half of this commit. The run printed "READY ROOM / >>> HUD / Stage 02 OK" and I began the experiment; there was no FLIGHT: line, because the flight check failed three times and fell through silently while the next line read like success. The game was frozen on a near-black screen (screen_id `other`, mean 10.8/2.8/2.1, frozen.py max_pixel_delta=0) and the pilot's every sample from t=0.0 to t=406.1 is byte-identical with speed 0. assert_stage.py could not have caught it: it reads the DEFINITION table, which is populated when the STAGE loads, independently of whether the mission is running. Recorded in nav-guards.md with the rule -- enforce the flight gate with a non-zero exit, and run the three-crop TIME liveness check before any experiment. --- docs/re/nav-guards.md | 33 +++++++++++++++++++++++++++++++++ tools/re-capture/pilot.py | 10 ++++++++++ 2 files changed, 43 insertions(+) diff --git a/docs/re/nav-guards.md b/docs/re/nav-guards.md index b679eee..2ea136d 100644 --- a/docs/re/nav-guards.md +++ b/docs/re/nav-guards.md @@ -104,3 +104,36 @@ this confirms it visually and pins the cursor's start position.) unit-tested, but "the route is now reproducible" rests on a single success. * Why the game window leaves the window tree during a load at all was not investigated — only guarded against. + +--- + +## ⚠️ `assert_stage` passing does NOT mean the run is in flight + +Measured the hard way 2026-08-27. A run printed: + +``` +MENU confirmed +loaded +READYROOM +>>> HUD +Stage 02 OK +``` + +…and I started an experiment against it. There is no `FLIGHT:` line in that +output. The flight check ran, failed to match three times, and fell through +**silently**; the very next line said `Stage 02 OK`, which read like success. + +It was not. The game was frozen on a near-black screen — `screen_id` `other`, +mean `(10.8, 2.8, 2.1)`, `frozen.py max_pixel_delta=0` — and the whole 460 s +experiment ran against a static world. The pilot's own log is the clearest +evidence: **every sample from t=0.0 to t=406.1 is byte-identical**, speed `0`, +the ship never even started moving. + +**Why the stage assertion did not catch it:** `assert_stage.py` reads the +DEFINITION table, and definitions are loaded when the *stage* loads — before, +and independently of, the mission actually starting. It answers "is the right +stage loaded", never "is the mission running". + +**Rule:** the flight gate must be *enforced*, not attempted — exit non-zero when +`screen_id` never reads `flight`, and take the three-crop TIME liveness check +before any experiment. Both were skipped here, and the run was lost. diff --git a/tools/re-capture/pilot.py b/tools/re-capture/pilot.py index 79abfdd..90c488d 100644 --- a/tools/re-capture/pilot.py +++ b/tools/re-capture/pilot.py @@ -115,6 +115,12 @@ KILL_TURRETS = os.environ.get("SYLPH_KILL_TURRETS") == "1" HUNT = os.environ.get("SYLPH_HUNT") == "1" PREFER = os.environ.get("SYLPH_PREFER", "") +# SYLPH_WEAKEST=1 scores a target by REMAINING HULL as well as range, so the +# pilot finishes what is already hurt instead of re-engaging whatever is +# nearest. Measured 2026-08-27 (ob-counts-marked-attackers.md): over ~8 minutes +# the pilot damaged 14 of 16 e010 attackers -- hulls 360..500 -- and killed +# NONE, because each carries 500 HP and the fire was spread across the squadron. +WEAKEST = os.environ.get("SYLPH_WEAKEST", "") not in ("", "0") class Pilot: @@ -399,6 +405,10 @@ class Pilot: # pointed at the thing the mission actually asks for. if PREFER: score *= 0.05 if PREFER in nm else 4.0 + if WEAKEST: + h = self.f32(off + HULL_OFF) + if h and h > 0.0: + score *= max(h, 1.0) / 500.0 if score < bestscore: best, bestscore = (off, nm, lead, lead - me_p, d, r), score return best