j134 fire fix: share ONE fire_tracker across tfil/ring/strafe/learned/surf (TR_FIRE_FIX, default on); env-gated TR_FIRE_DIAG alignment trace

This commit is contained in:
2026-09-26 12:40:19 +02:00
parent bd57db6d63
commit 6ad5d99922
11 changed files with 435 additions and 214 deletions
+42 -7
View File
@@ -48,6 +48,19 @@ import sys
# a corpus artifact only.
CORPUS_EVENT_SHIFT = 1
# Per-mover detector windows, as wired in j134. Every mover now calls the SAME
# shared `movement_harness/fire_tracker.nim`; only the window it supplies differs
# (`strafe` is the j133 reference). The correction and the over-cap SPLIT are
# shared, so the FIXED catch rate must be identical for all of them; the SHIPPED
# rate can differ slightly where the windows differ.
MOVER_WINDOWS = {
"tfil": (0.09, 3.01),
"ring": (0.09, 3.01),
"strafe": (0.09, 3.01),
"learned": (0.09, 3.01),
"surf": (0.10, 3.00),
}
def load_run(path: str):
rows, events = [], []
@@ -66,7 +79,7 @@ def load_run(path: str):
return rows, events, rounds
def detect(rows, events, rounds, fix: bool):
def detect(rows, events, rounds, fix: bool, lo: float = 0.09, hi: float = 3.01):
"""Replay the detector; return {global_tick: [powers]} of created waves."""
starts = {r["round"]: r["startTick"] for r in rounds}
by_global = {r["startTick"] + k: (r["round"], k)
@@ -118,11 +131,11 @@ def detect(rows, events, rounds, fix: bool):
if fix:
drop = raw + bonus.get((rn, rt + CORPUS_EVENT_SHIFT), 0.0) \
- dealt.get((rn, rt + CORPUS_EVENT_SHIFT), 0.0)
if fix and drop > 3.01:
if fix and drop > hi:
n = int(math.ceil(drop / 3.0))
for _ in range(n):
waves[t].append(drop / n)
elif 0.09 <= drop <= 3.01:
elif lo <= drop <= hi:
waves[t].append(drop)
return waves, energy, eowner
@@ -139,8 +152,8 @@ def fires_of(events, rounds, eowner):
return out
def score(rows, events, rounds, fix: bool):
waves, energy, eowner = detect(rows, events, rounds, fix)
def score(rows, events, rounds, fix: bool, lo: float = 0.09, hi: float = 3.01):
waves, energy, eowner = detect(rows, events, rounds, fix, lo, hi)
starts = {r["round"]: r["startTick"] for r in rounds}
by_global = {r["startTick"] + k: (r["round"], k)
for r in rounds for k in range(r["count"])}
@@ -163,7 +176,7 @@ def score(rows, events, rounds, fix: bool):
drop = raw
if fix:
drop = raw # classification below only needs the miss kind
if raw > 3.01:
if raw > hi:
cats["reject_high"] += 1
else:
cats["reject_low"] += 1
@@ -228,7 +241,7 @@ def main() -> int:
caught += c
lat.update(l)
cats.update(k)
tag = "FIXED (TR_STRAFE_FIRE_FIX=1)" if fix else "SHIPPED (TR_STRAFE_FIRE_FIX=0)"
tag = "FIXED (TR_FIRE_FIX=1)" if fix else "SHIPPED (TR_FIRE_FIX=0)"
out(f"\n== {tag} ==")
out(f"true enemy fires: {total}")
out(f"caught (wave on the fire's own tick): {caught} "
@@ -247,6 +260,28 @@ def main() -> int:
out("lst increment histogram: " + ", ".join(
f"{k}:{v} ({100.0 * v / total:.2f}%)" for k, v in sorted(incs.items())))
# ── per-mover table (j134) ──────────────────────────────────────────────
# Every mover now calls the shared `movement_harness/fire_tracker.nim`; only
# the window differs. This is the propagation proof: the SHIPPED path leaves
# each mover blind to the same 1.11%, the FIXED path is 100% for all.
out("\n== per-mover catch rate (shared fire_tracker, j134) ==")
out(f"{'mover':<8} {'window':<14} {'shipped':>9} {'fixed':>9} "
f"{'blind_before':>13} {'blind_after':>12}")
for name, (wlo, whi) in MOVER_WINDOWS.items():
per = {}
for fix in (False, True):
total = caught = 0
for path in runs:
rows, events, rounds = load_run(path)
t, c, _, _ = score(rows, events, rounds, fix, wlo, whi)
total += t
caught += c
per[fix] = (total, caught)
total, caught0 = per[False]
_, caught1 = per[True]
out(f"{name:<8} {f'{wlo}-{whi}':<14} {caught0 / total:>9.5f} "
f"{caught1 / total:>9.5f} {total - caught0:>13} {total - caught1:>12}")
if args.report:
os.makedirs(os.path.dirname(args.report) or ".", exist_ok=True)
with open(args.report, "w") as f: