This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/tools/re-capture/wave6_probe.py
Sylpheed RE agent 171f682b50 re: six runs with no arrival, and an accidental no-pilot control
wave6_probe refuses to interpret a run whose roster count is not the reproduced
baseline of 116, per the discard rule. This run passed and its first sample is
identical to the earlier link run: 116 records, 300 craft, 41 deployed, strength
histogram 2x24, 4x1, 8x4, 18x12. The deployment is deterministic at mission
start.

The pilot failed to bind, which accidentally supplied the control condition the
kill-versus-no-kill experiment needed. With nobody flying, not one craft was
destroyed in four minutes -- exactly 300 across all 22 samples -- against 16-20
losses in each piloted run. So losses are attributable to the player being in
the fight, and NPC crossfire destroys nothing by itself. That was an open
question two iterations ago.

Still no arrival. Zero 0 -> n transitions in either condition, across roughly
fifteen minutes of cumulative Stage 02 flight and windows up to 240 s. The 75
records holding no craft at mission start still hold none at the end. Against a
route table scheduling phase-1 arrivals at t = 90/120/170/210/240, that is now a
strong negative rather than a null result.

Three readings survive: the timetable's t is not seconds (at 30 Hz the whole
phase-1 schedule finishes inside 8 s, before any first sample); arrivals are
event-gated and no run supplied the trigger; or the mission is not advancing its
phase clock at all.

The third has never been checked and is the cheapest to eliminate, so it is now
the prime suspect. Nothing in six runs has confirmed that mission time advances:
the craft count freezes without a pilot, REMAINING OB has never read as a
counter, and no clock has been located. Every "no arrival" observation is
consistent with a scheduler that simply is not running, in which case the
arrival results so far are measuring a stopped clock.
2026-08-24 14:42:36 +00:00

105 lines
4.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""Per-record arrival/loss watch against the verified 116/300 baseline.
Supersedes wave5_probe.py, which reported 42 records in a run nobody could
reproduce. Changes:
* refuses to interpret a run whose roster count is not the reproduced
baseline (mission-per-record-strength.md: discard, do not interpret);
* keys records by file offset rather than by primary VA;
* samples faster and for longer, since losses appear at ~1 per 10 s and no
arrival has been seen in five runs.
An arrival is a record going 0 -> n; a loss is n -> n-1.
"""
import os, sys, time, struct, collections, importlib.util
SD = __file__.rsplit('/', 1)[0]
sys.path.insert(0, SD)
import gmem, gworld, entities2
_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)
DELTA, WIN, LINK = 0x130, 0x400, 0x08
BASELINE = 116
def scan_vt(fd, size, vt):
out = []
for a, b in gmem.extents(fd, size):
pos = a
while pos < b:
m = min(1 << 24, b - pos)
blob = os.pread(fd, m, pos)
i = blob.find(vt)
while i != -1:
if (pos + i) % 4 == 0: out.append(pos + i)
i = blob.find(vt, i + 1)
pos += m
return sorted(out)
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
while pos < hi:
m = min(1 << 24, hi - pos)
blob = os.pread(fd, m, pos)
for k in range(0, len(blob) - 3, 4):
if blob[k:k+4] not in defs: continue
tot += 1
head = os.pread(fd, WIN, pos + k - DELTA)
for j in range(0, len(head) - 3, 4):
(p,) = struct.unpack_from('>I', head, j)
if p in want:
per[want[p]] += 1; break
pos += m
return per, tot
def main():
secs = int(sys.argv[1]) if len(sys.argv) > 1 else 240
every = int(sys.argv[2]) if len(sys.argv) > 2 else 10
w = gworld.World(); fd = w.fd
defs = entities2.definitions(w)
if not defs: print('NOT IN A MISSION'); return 2
roster = scan_vt(fd, w.size, ROSTER_VT)
print('roster records: %d (baseline %d)' % (len(roster), BASELINE))
if len(roster) != BASELINE:
print('DISCARD: roster count is not the reproduced baseline. '
'Per mission-per-record-strength.md this run is not interpreted.')
return 3
f = os.fdopen(os.dup(fd), 'rb')
label, want = {}, {}
for o in roster:
va = gmem.primary_va(o)
if va is None: continue
label[o] = wave3.resolve_id(f, w.size, o)[0] or '?'
want[va + LINK] = o
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:
per, tot = sample(fd, defs, want)
el = round(time.time() - t0)
if prev is None:
print('t=%4ds craft=%d deployed=%d/%d strengths %s'
% (el, tot, len(per), len(roster),
sorted(collections.Counter(per.values()).items())), flush=True)
else:
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)
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))
for o, a, b in los:
print(' loss %-30s %d -> %d' % (label.get(o, '?'), a, b), flush=True)
log.write('%d\tloss\t%#x\t%s\t%d\t%d\n' % (el, o, label.get(o, '?'), a, b))
log.flush()
prev = per
time.sleep(max(0, every - (time.time() - t0 - el)))
log.close()
print('\nTOTAL arrivals=%d losses=%d over %ds' % (arr_n, loss_n, secs))
return 0
if __name__ == '__main__':
sys.exit(main())