Files
SirRoboGarage/docs/tfil_hold_when_trapped_ab.md
SirStone 94ffc63160 j153: the open-loop ruler for "hold when trapped" + the A/B proposal (NOT run)
Measures, on the recorded fixtures and with NO counterfactual replay: how long
until a safe tile appears at a forced pick, whether the enemy had just fired,
whether our own tile is already hot, and all three split by distance. Registers
nothing and changes no default: the knob TR_TFIL_HOLD_WHEN_TRAPPED and its
implementation live in the concurrently edited working tree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-27 10:16:53 +02:00

4.3 KiB
Raw Permalink Blame History

j153 proposal (NOT RUN) — TR_TFIL_HOLD_WHEN_TRAPPED: hold when no safe tile exists

Status: awaiting the owner's approval. No battle, no A/B arm, no tournament has been started. The knob exists, is registered, defaults to today's behaviour byte-for-byte, and is guarded offline.

The owner's claim

"if no tile is found to go, to not choose the less dangerous, but to stay still! the next tick probably the situation already changed and we did not commit to any dangerous place."

Today, when the safe set (pathMaxHeat <= PathDangerThreshold, after the CoolestLevels = 2 filter) is too small to draw from, the picker promotes the 2 least-hot blocked tiles and moves to one of them. The proposal replaces that with speed 0.0 for that tick only.

The knob

env default meaning
TR_TFIL_HOLD_WHEN_TRAPPED 0 (off) 1 = hold when the safe set is empty; today = promote the 2 least-hot blocked tiles
  • One tick, never latched. The hold is taken at the pick site, and the pick site only runs when commitTicks == 0, so the next tick re-evaluates the field from scratch. There is deliberately no max-hold knob in j153: a counter can only add a way to get stuck. (j154, in flight, adds TR_TFIL_HOLD_MAX_TICKS = 0 = off as a separate default-off knob.)
  • It never interrupts a live commitment. The hold replaces a replan, not a commitment in progress: with TR_TFIL_COMMIT_ARRIVAL=1 or TR_TFIL_NOREV_SPEED=4 armed the two do not fight, because the hold branch is downstream of the commitment block and only runs at commitTicks == 0.
  • The gun keeps firing. computeMove never emits a fire command (the gun lives in the bot's go() loop), and the hold returns after the bullet tracking, so the fire tracker's state on a held tick is bit-identical to a non-held tick (guarded in common_libs/tests/test_tfil_commit_env.nim).

What the offline harness can and cannot say

Per docs/offline_harness_trust.md the replay harness is trustworthy only for per-gun single-tick prediction on a fixed enemy trajectory; it scored 0/6 on closed-loop questions. "Hold vs move" is a counterfactual closed-loop question, so this document contains no damage-taken comparison for holding. Only open-loop descriptors of the recorded field are reported (common_libs/tests/measure_tfil_hold_window.nim).

Proposed A/B (needs approval)

TOURNAMENT_NIMCACHE=/tmp/nc_j153 \
tools/ab/tournament_run.sh \
  --arms    tools/ab/arms_hold_trapped.txt \
  --panel   tools/ab/panel_movement.txt \
  --runs 14 --rounds 7 --conc 7 --wait-arena 45 \
  --reference hold0 \
  --outdir /tmp/ab/j153_hold

python3 tools/ab/tournament_analyze.py /tmp/ab/j153_hold --reference hold0

Arms file (frozen 15-opponent movement panel, 14 runs/arm, 7 rounds):

hold0    |                                      | control = today's promote-the-2 fallback
hold1    | TR_TFIL_HOLD_WHEN_TRAPPED=1         | hold one tick when the safe set is empty
hold8    | TR_TFIL_HOLD_WHEN_TRAPPED=1 TR_TFIL_HOLD_MAX_TICKS=8 | bounded hold (only if j154 lands)

Primary metrics: damage/run and round-win rate (NOT hit rate — a movement arm's value flows through the closed loop). Mechanism metrics: incoming hit rate, % of picks held, mean distance-to-enemy at a hold, tick-share at speed 0.

MDE, stated up front: ~0.28 wins/run at 14 runs/arm; a two-arm session is ~1 h. Resolving ~0.10 wins/run needs ~2.2 h / ~2,900 battles. A 1 h two-arm run can only reject effects at or above ~0.28 wins/run — anything smaller is a null by construction, and must be reported as such.

Prior, stated plainly: four mechanism-positive / outcome-null results in a row on this campaign. A null is the most likely outcome. A null with a mechanism hit (holds fire, distance at hold is large) would mean: holding is achievable and does not by itself buy rounds; ship nothing. A null without a mechanism hit means the arm never bound and the A/B is void, not negative.

Decision each duration supports

duration supports
1 h (2 arms × 14 runs) reject/accept only ≥0.28 wins/run. Mechanism check only.
2.2 h (~2,900 battles, 3 arms) resolve ~0.10 wins/run. Still not a small-effect test.
any null no change to the shipped default. TR_TFIL_HOLD_WHEN_TRAPPED stays 0.