re: eliminate an enemy squadron — no wave follows; add a stall witness
SYLPH_KEEPOUT makes the hunt keep-out a knob instead of a hard-coded 600. At 1400 the pilot both kills and survives: hull 1500 and escorted asset 100 % for the whole run, ENGAGE throughout, eight loss events against seven in the run where it died at 83 s. That let the elimination test actually run. An e007 Turret squadron was ground 18 -> 10 -> 8 -> 4 -> 2 -> 0, reaching zero at t=163 s with deployed falling 41 to 40 -- an enemy squadron destroyed outright for the first time in nine runs. No arrival followed. ARRIVALS=0 at every sample including all those after t=163. The hypothesis that a wave is released when a squadron is wiped out rather than merely damaged does not survive its first test. Two further losses occurred at t=176 and t=202, so the mission was demonstrably still live and still processing kills; it simply produced no arrival. This refutes elimination-of-one-squadron as the trigger. It does not refute event-gating generally -- a threshold across several squadrons, an objective completion, or a specific squadron could all still be the gate. The valid window is smaller than the log suggests, about 90 s after the elimination rather than 143 s, because the guest stalled around t=255 s. The pilot's telemetry gives it away: the last 400 log lines contain one distinct speed value against 236 in the first 400, with no timestamp gaps -- the process kept logging while the game stopped advancing. Nothing in the probe output distinguished a stall from a quiet mission, and the same ambiguity affects the trailing flat samples of earlier runs. wave6_probe now locates a counter advancing at frame rate, samples it each tick and prints GUEST STALLED when it fails to advance, so future runs validate themselves. Implemented but not yet exercised in a run.
This commit is contained in:
@@ -382,8 +382,17 @@ search cannot find a *schedule*.
|
||||
probe duration.** 🔴 Harness bug found+fixed: a `sed` had stripped the probe
|
||||
args, so every derived probe ran on defaults — the previous "cut by the turn
|
||||
timeout" claim was wrong (it hit its own 240 s default).
|
||||
**Next: a pilot that kills AND survives** (hunt turrets but keep evade/retire;
|
||||
keep-out between 600 and 2500) — tuning, not discovery.
|
||||
✅🔴 **DONE + REFUTED (2026-08-24)**: `SYLPH_KEEPOUT=1400` gives a pilot that
|
||||
kills and survives (hull 1500, asset 100 %, 8 losses). An enemy squadron was
|
||||
**eliminated** — `e007 Turret 2→0` at t=163 s, `deployed` 41→40, first ever —
|
||||
and **no arrival followed** in ~90 s of valid observation (two further losses in
|
||||
that window prove the mission was live). ⇒ *"a wave is released when a squadron
|
||||
is wiped out"* is **refuted**; event-gating generally is not (thresholds,
|
||||
objectives, specific squadrons untested). 🔴 **Guest stalled at ~t=255 s** —
|
||||
last 400 pilot lines have 1 distinct speed vs 236 in the first 400 — so trailing
|
||||
flat samples in ANY earlier run may be a frozen guest, not a quiet mission.
|
||||
Probe now carries a frame-rate tick witness printing `*** GUEST STALLED ***`
|
||||
(implemented, **not yet run**).
|
||||
⚠️ The ~210 s title movie at boot is the binding constraint on observable game
|
||||
time per turn.
|
||||
Earlier framing:
|
||||
|
||||
@@ -200,3 +200,75 @@ extremes: survival mode kills nothing in 240 s, hunt mode kills steadily and die
|
||||
at 83 s. A middle setting — hunt turrets but keep the evade/retire behaviour, or
|
||||
a keep-out between 600 and 2500 — is the obvious next step, and it is a tuning
|
||||
change rather than a new discovery.
|
||||
|
||||
---
|
||||
|
||||
# ✅ The elimination test ran — and refutes the elimination trigger (2026-08-24)
|
||||
|
||||
Status: ✅ an enemy squadron was wiped out and observed; 🔴 **no arrival
|
||||
followed**, so "a wave is released when a squadron is eliminated" is refuted for
|
||||
this case; ✅ the keep-out tuning gave a surviving pilot that still kills;
|
||||
🔴 the guest stalled late in the run, which bounds the valid window and is now
|
||||
detected automatically.
|
||||
|
||||
## ✅ The tuning worked
|
||||
|
||||
`SYLPH_KEEPOUT` makes the hunt keep-out a knob rather than a hard-coded 600.
|
||||
At **1400** the pilot both kills and survives — hull 1500 and escorted asset
|
||||
100 % for the entire run, `ENGAGE` throughout, 8 loss events against 7 in the
|
||||
run where it died at t = 83 s.
|
||||
|
||||
## ✅ An enemy squadron reached zero
|
||||
|
||||
```
|
||||
t= 30s loss UN_e007_ADAN_Turret 18 -> 10
|
||||
t= 44s loss UN_e007_ADAN_Turret 10 -> 8
|
||||
t= 96s loss UN_e007_ADAN_Turret 8 -> 4
|
||||
t=124s loss UN_e007_ADAN_Turret 4 -> 2
|
||||
t=150s loss UN_e010_ADAN_Attacker_S 8 -> 6
|
||||
t=163s loss UN_e007_ADAN_Turret 2 -> 0 <-- ELIMINATED, deployed 41 -> 40
|
||||
t=176s loss UN_f106_TCAF_Destroyer 4 -> 2
|
||||
t=202s loss UN_e007_ADAN_Turret 18 -> 16
|
||||
```
|
||||
|
||||
An enemy squadron destroyed outright, for the first time in nine runs.
|
||||
|
||||
## 🔴 No arrival followed — the elimination trigger is refuted
|
||||
|
||||
`ARRIVALS=0` at every sample, including all of those after t = 163. The
|
||||
hypothesis from the previous iteration — that a wave is released when a squadron
|
||||
is *wiped out* rather than merely damaged — does not survive its first test.
|
||||
|
||||
**The window matters and is smaller than it looks:** valid observation after the
|
||||
elimination is about **90 seconds**, not the 143 s the log appears to show, for
|
||||
the reason below. Within that 90 s two further losses occurred (t = 176, t = 202),
|
||||
so the mission was demonstrably still live and still processing kills — it simply
|
||||
produced no arrival.
|
||||
|
||||
This refutes elimination-of-one-squadron as *the* trigger. It does not refute
|
||||
event-gating generally: a threshold across several squadrons, an objective
|
||||
completion, or a specific squadron could all still be the gate.
|
||||
|
||||
## 🔴 The guest stalled at ~t = 255 s, and flat samples look identical to a quiet mission
|
||||
|
||||
The pilot's own telemetry gives it away: the **last 400 log lines contain one
|
||||
distinct speed value, against 236 in the first 400**, with no timestamp gaps.
|
||||
The process kept logging; the game stopped advancing.
|
||||
|
||||
Every sample after roughly t = 255 s is therefore a frozen guest, not a quiet
|
||||
mission — and nothing in the probe's output distinguished the two. The same
|
||||
ambiguity affects any earlier run's trailing flat samples.
|
||||
|
||||
**Fixed:** `wave6_probe.py` now locates a counter that advances at frame rate
|
||||
(the technique from [mission-clock-advances.md](mission-clock-advances.md)),
|
||||
samples it every tick, and prints `*** GUEST STALLED ***` when it fails to
|
||||
advance. This makes every future run self-validating. It is implemented but has
|
||||
**not yet run**, so it is unverified.
|
||||
|
||||
## What is still open
|
||||
|
||||
* Whether any event gates arrivals — elimination of a single squadron is out,
|
||||
but thresholds, objectives and specific squadrons are untested.
|
||||
* Whether an arrival is observable at all. Nine runs, zero `0 → n`.
|
||||
* Re-examining earlier "flat" results now that a stall and a quiet mission are
|
||||
known to look the same.
|
||||
|
||||
@@ -123,8 +123,12 @@ class Pilot:
|
||||
CONE_MIN = math.radians(2.0)
|
||||
CONE_MAX = math.radians(25.0) # close-in the target subtends a lot; let it
|
||||
# Keep-out from things that shoot back. Under SYLPH_HUNT the turrets ARE the
|
||||
# targets, so standing off 2.5 km from them just guarantees no kills.
|
||||
TURRET_KEEPOUT = 600.0 if HUNT else 2500.0
|
||||
# targets, so standing off 2.5 km from them guarantees no kills -- but 600
|
||||
# cost the pilot its life at t=83 s and froze the mission for the remaining
|
||||
# 220 s of the run (mission-arrival-watch.md). The usable observation window
|
||||
# is player survival, so this is a real trade and not a free knob. Tunable.
|
||||
TURRET_KEEPOUT = float(os.environ.get(
|
||||
"SYLPH_KEEPOUT", "1400" if HUNT else "2500"))
|
||||
EVADE_QUIET = 5.0 # seconds without damage before re-engaging
|
||||
RETIRE_FRAC = 0.30 # hull fraction that sends us home
|
||||
HZ = 8.0
|
||||
|
||||
@@ -19,6 +19,11 @@ _w3 = importlib.util.spec_from_file_location('w3', SD + '/wave3_probe.py')
|
||||
wave3 = importlib.util.module_from_spec(_w3); _w3.loader.exec_module(wave3)
|
||||
|
||||
ROSTER_VT = struct.pack('>I', 0x820AF030)
|
||||
# A guest that has stalled produces flat samples that read exactly like a quiet
|
||||
# mission -- that happened at t~255 in the keep-out run and was only caught by
|
||||
# eyeballing the pilot log afterwards. timer_probe found counters advancing at
|
||||
# frame rate; sampling one of them makes every run self-validating.
|
||||
TICK_LO, TICK_HI = 0xBC000000, 0xBE000000
|
||||
DELTA, WIN, LINK = 0x130, 0x400, 0x08
|
||||
BASELINE = 116
|
||||
|
||||
@@ -36,6 +41,20 @@ def scan_vt(fd, size, vt):
|
||||
pos += m
|
||||
return sorted(out)
|
||||
|
||||
def find_tick(fd, dt=3.0):
|
||||
"""One word that advances steadily: the guest-is-running witness."""
|
||||
lo, hi = gmem.va_to_off(TICK_LO), gmem.va_to_off(TICK_LO + 0x400000)
|
||||
a = os.pread(fd, hi - lo, lo)
|
||||
time.sleep(dt)
|
||||
b = os.pread(fd, hi - lo, lo)
|
||||
best = None
|
||||
for k in range(0, min(len(a), len(b)) - 3, 4):
|
||||
va = struct.unpack_from('>I', a, k)[0]
|
||||
vb = struct.unpack_from('>I', b, k)[0]
|
||||
if va < vb and (vb - va) / dt > 5 and (vb - va) < 1 << 20:
|
||||
best = (lo + k, (vb - va) / dt); break
|
||||
return best
|
||||
|
||||
def sample(fd, defs, want):
|
||||
lo, hi = gmem.va_to_off(entities2.ENT_VA_LO), gmem.va_to_off(entities2.ENT_VA_HI)
|
||||
per = collections.Counter(); tot = 0; pos = lo
|
||||
@@ -72,6 +91,10 @@ def main():
|
||||
if va is None: continue
|
||||
label[o] = wave3.resolve_id(f, w.size, o)[0] or '?'
|
||||
want[va + LINK] = o
|
||||
tick = find_tick(fd)
|
||||
if tick: print('tick witness at %#x, ~%.1f/s' % (tick[0], tick[1]))
|
||||
else: print('WARNING: no advancing counter found; stalls cannot be detected')
|
||||
last_tick = struct.unpack('>I', os.pread(fd, 4, tick[0]))[0] if tick else 0
|
||||
log = open('/tmp/wave6.tsv', 'w'); log.write('t\tkind\toff\tunit\tfrom\tto\n')
|
||||
prev = None; t0 = time.time(); arr_n = loss_n = 0
|
||||
while time.time() - t0 < secs:
|
||||
@@ -85,8 +108,14 @@ def main():
|
||||
arr = [(o, per[o]) for o in per if prev.get(o, 0) == 0 < per[o]]
|
||||
los = [(o, prev[o], per.get(o, 0)) for o in prev if per.get(o, 0) < prev[o]]
|
||||
arr_n += len(arr); loss_n += len(los)
|
||||
print('t=%4ds craft=%d deployed=%d ARRIVALS=%d losses=%d (cum %d/%d)'
|
||||
% (el, tot, len(per), len(arr), len(los), arr_n, loss_n), flush=True)
|
||||
stalled = ''
|
||||
if tick:
|
||||
now = struct.unpack('>I', os.pread(fd, 4, tick[0]))[0]
|
||||
if now <= last_tick: stalled = ' *** GUEST STALLED ***'
|
||||
last_tick = now
|
||||
print('t=%4ds craft=%d deployed=%d ARRIVALS=%d losses=%d (cum %d/%d)%s'
|
||||
% (el, tot, len(per), len(arr), len(los), arr_n, loss_n, stalled),
|
||||
flush=True)
|
||||
for o, c in arr:
|
||||
print(' ARRIVAL %-30s 0 -> %d' % (label.get(o, '?'), c), flush=True)
|
||||
log.write('%d\tarrival\t%#x\t%s\t0\t%d\n' % (el, o, label.get(o, '?'), c))
|
||||
|
||||
@@ -7,7 +7,7 @@ SECS="${1:-180}"; EVERY="${2:-10}"; HUNT="${3:-1}"
|
||||
CFG=/tmp/nav-live.json
|
||||
"$SD/launch_mission.sh" fly || { echo "BOOT FAILED"; exit 1; }
|
||||
if python3 "$SD/entities2.py" self 0x130 "$CFG" >/dev/null 2>&1; then
|
||||
SYLPH_HUNT="$HUNT" SYLPH_KILL_TURRETS=1 nohup python3 "$SD/pilot.py" "$CFG" "$SECS" \
|
||||
SYLPH_HUNT="$HUNT" SYLPH_KILL_TURRETS=1 SYLPH_KEEPOUT="${SYLPH_KEEPOUT:-1400}" nohup python3 "$SD/pilot.py" "$CFG" "$SECS" \
|
||||
</dev/null >/tmp/live-pilot.log 2>&1 &
|
||||
PILOT=$!; echo "--- pilot (SYLPH_HUNT=$HUNT)"
|
||||
else PILOT=""; echo "--- BIND FAILED, no pilot"; fi
|
||||
|
||||
Reference in New Issue
Block a user