j134 ledger: propagated fire fix to all 5 movers via one shared fire_tracker (TR_FIRE_FIX), per-mover catch table 98.888->100, live tick alignment measured (correction belongs on event_tick+2)
This commit is contained in:
@@ -2540,3 +2540,110 @@ the fix.
|
||||
**Status: the shipped movement default is UNCHANGED (`TR_MOVEMENT=strafe`); the
|
||||
detector fix is ON by default behind `TR_STRAFE_FIRE_FIX` (revert with
|
||||
`TR_STRAFE_FIRE_FIX=0`).**
|
||||
|
||||
## Fire fix propagated to all movers
|
||||
|
||||
Job **j134** (2026-09-26; commits `6ad5d99`, `8827338`). Scope: ModularBot /
|
||||
modules / tuning / the test harness only.
|
||||
|
||||
### The shared helper — one implementation, not five
|
||||
|
||||
The j133 detector was fixed **only in `strafe.nim`**; the other four movers kept
|
||||
their own copy of the same broken `0.09 <= drop <= 3.01` classifier. Four copies
|
||||
is exactly how the bug survived, so the fix now lives **once** in
|
||||
`common_libs/movement_harness/fire_tracker.nim` (`FireTracker`). Each mover owns
|
||||
its own wave geometry and spawn code but calls `m.fire.detect(id, energy, lo,
|
||||
hi, fix)`; the mover supplies its **shipped window** (`0.09..3.01` for tfil /
|
||||
tfil_ring / strafe / learned, `0.1..3.0` for surf) so the fix-off path is the
|
||||
old code exactly, and its own switch so the tracker holds **no** enable flag.
|
||||
|
||||
One global switch, `TR_FIRE_FIX` (default **ON**), gates every mover; STRAFE also
|
||||
still honours `TR_STRAFE_FIRE_FIX` (j133 back-compat) and is on only when **both**
|
||||
are on. Both names are registered in `env_report.nim` + `knownEnvNames()`
|
||||
(`TR_FIRE_DIAG`, the live trace, is registered too).
|
||||
|
||||
`ModularBot.nim` forwards both events to **every** mover (`onHitByBullet` ->
|
||||
`e.bullet.power`; `onBulletHit` -> `e.damage`); previously only STRAFE received
|
||||
them.
|
||||
|
||||
### TASK C — per-mover catch rate (offline, 70-battle corpus, 67 065 true enemy fires)
|
||||
|
||||
Every mover now calls the same `FireTracker`; only the window differs, so the
|
||||
fixed rate must be (and is) identical. The SHIPPED rate differs by one shot for
|
||||
`surf` because its window is `0.1..3.0` vs `0.09..3.01`. Full report:
|
||||
`common_libs/tests/fixtures/strafe_fire_catch_report.txt` (regenerated by
|
||||
`common_libs/tests/measure_strafe_fire_catch.py`, extended with the per-mover
|
||||
table).
|
||||
|
||||
| mover | window | shipped | fixed | blind before | blind after |
|
||||
|---|---|---:|---:|---:|---:|
|
||||
| tfil | 0.09–3.01 | 0.98888 | **1.00000** | 746 | **0** |
|
||||
| tfil_ring | 0.09–3.01 | 0.98888 | **1.00000** | 746 | **0** |
|
||||
| strafe | 0.09–3.01 | 0.98888 | **1.00000** | 746 | **0** |
|
||||
| learned | 0.09–3.01 | 0.98888 | **1.00000** | 746 | **0** |
|
||||
| surf | 0.10–3.00 | 0.98886 | **1.00000** | 747 | **0** |
|
||||
|
||||
**Every mover is at 100%.** No mover was left unfixed. The shipped path is still
|
||||
byte-identical with the switch off: `test_tfil_commit_env.nim` replays the
|
||||
15 000+-tick TFIL trajectory against the pre-change golden with `TfilFireFix =
|
||||
false` and still matches every call/speed/turnRate/target/commitTicks.
|
||||
|
||||
### TASK B — live tick alignment (this is where the fix was wrong, and fixed)
|
||||
|
||||
One real 7-round battle, strafe, vs `/tmp/tr_bots/WaveSurferGF`, with the
|
||||
env-gated `TR_FIRE_DIAG=1` trace (kept; default off). **MEASURED:** the server
|
||||
emits the hit event on turn N but applies the energy change to turn **N+1**'s
|
||||
reading, and the bot's event handler runs with `bot.tick = getTurn - 1`. So the
|
||||
correction must land on the reading **two `bot.tick`s after** the event, not the
|
||||
next one. Paired post-fix lines (verbatim):
|
||||
|
||||
```
|
||||
[firediag] EV dmg tick=62 getTurn=63 damage=4.0
|
||||
[firediag] READ tick=64 raw=4.0 bonus=0.0 dealt=4.0 <- correction on the reading that carries the +4.0 drop
|
||||
[firediag] EV hit tick=109 getTurn=110 power=1.2437
|
||||
[firediag] READ tick=111 raw=-3.731198... bonus=3.731198... dealt=0.0
|
||||
```
|
||||
|
||||
Before this job the correction was applied on the **immediately next** reading:
|
||||
on the same battle that put `bonus=5.803` on a reading with `raw=0.0` (a
|
||||
spurious wave) while the real `raw=-5.803` gain one tick later was left
|
||||
uncorrected — i.e. j133's fix was **correct in the offline model but mis-timed
|
||||
live**. Fixed by a one-slot double buffer in `FireTracker` (`incoming` -> rotated
|
||||
`pending` at `endScan`), which makes the live path agree with the corpus model.
|
||||
|
||||
Aggregate over the whole trace: enemy-hit corrections on the reading of
|
||||
`event_tick+2` **44 aligned, 8 events ended a round with no later reading, 3
|
||||
misaligned (two simultaneous hits in one turn, matched as one sum)**; our-damage
|
||||
corrections **52 aligned, 6 round-boundary, 0 misaligned**.
|
||||
|
||||
### Guard tests + clean-archive compile
|
||||
|
||||
`git archive HEAD | tar -x` into a clean dir, then:
|
||||
`ModularBot` compiles; `test_strafe_fire_fix` **14/14**, `test_tfil_commit_env`
|
||||
**30/30**, `test_tfil_ring_weights` **24/24**, `test_wavesurfer_velocity`
|
||||
**7/7**, `test_learned_surfer` **24 checks / 0 failures** — **99 checks, 0
|
||||
failures**.
|
||||
|
||||
### Direct answers
|
||||
|
||||
1. **Are all movers now at 100% catch?** **Yes.** tfil, tfil_ring, strafe,
|
||||
learned, and surf all go 0.98888 (or 0.98886 for surf) -> **1.00000** on the
|
||||
67 065-fire corpus; each was blind to 746/747 shots, now 0.
|
||||
2. **Is the live tick alignment confirmed?** **Yes — and it was NOT same-turn.**
|
||||
The server applies the energy change one turn after the event, so the
|
||||
correction is applied on the reading two `bot.tick`s after the event; 44/47
|
||||
in-window hit corrections and 52/52 in-window damage corrections land on the
|
||||
exact reading that carries the change (the rest are round boundaries or
|
||||
simultaneous events). The j133 guess that "the reading is same-turn and the
|
||||
corpus +1 is a capture artifact" was **wrong**; the fix now encodes the
|
||||
measured lag.
|
||||
|
||||
### MEASURED vs INFERRED
|
||||
|
||||
**MEASURED:** the per-mover catch table on 67 065 fires; the live paired
|
||||
event/reading lines and the `event_tick+2` alignment counts from one real
|
||||
7-round battle; the two-buffer fix; the clean-archive compile; 99/99 guard
|
||||
checks. **INFERRED:** that the correction size (`3*power`, `damage`) is
|
||||
unchanged — it is read straight from the server events, not re-derived.
|
||||
**NOT MEASURED:** the live movement/damage effect of the fix (the change is
|
||||
~1.11% of fires, far below any panel's MDE; no panel was run, per scope).
|
||||
|
||||
Reference in New Issue
Block a user