STRAFE: draw the heat field, real bullet danger, corrected ring comment

Three defects the owner hit as "no heat tiles anymore" under TR_MOVEMENT=strafe.

1. The strafe overlay drew ONLY the tiles on its strafe line, so the computed
   heat field was essentially invisible. It now draws the WHOLE field exactly as
   TFIL does (every non-zero tile, yellow->orange->red ramp by field max, integer
   value label) behind the same debugGraphics flag, with TR_STRAFE_HEAT_GRID=0 to
   hide it. The strafe overlays draw on top, unchanged.

2. STRAFE carried the SHIPPED bullet constants (core 10 / aura 5), so a bullet's
   own heat sat exactly ON PathDangerThreshold (10.0) and a bullet was never
   dangerous on its own in this mover; it only ever bit through its corridor.
   Defaults are now the retune's 20/10, exposed as TR_STRAFE_BULLET_CORE /
   TR_STRAFE_BULLET_AURA.

3. The ring mover's header documented CorridorHeat 5.0 / WallHotness 10.0 while
   the code has always been 10.0 / 15.0. A job read the comment and handed out
   sub-threshold heat values, which emptied the field. The comment now states the
   real values and their actual behaviour; no code values changed.

Also sets strafe's heat defaults to the retune shape (bullet 20/10, corridor 10,
wall 15/5, pillar 0), documented with the reason.

Gate A re-run (j110, offline DrussGT fixture, measure_strafe_gates.nim):
  corrected DEFAULT : 24.6% of picks with ZERO safe tile, mean 11.17 safe
  j108 shipped field: 63.4% / 3.70   (reproduced exactly)
  j108 ring retune  :  8.1% / 18.41  (reproduced exactly)
  bullet isolated   : 11.4% / 17.07
The corrected default beats the shipped field but is WORSE than j108's retune
row: the bullet retune alone costs 8.1 -> 11.4, the corridor/wall retune accounts
for the rest. That is the deliberate price of making a bullet dangerous.

Guards green: test_env_report 24 PASS, test_tfil_commit_env 30 PASS (shipped TFIL
default untouched, byte-for-byte), test_tfil_ring_weights 24 PASS. The three new
knobs are registered in the boot env report so the tree-scan guard stays clean.
This commit is contained in:
2026-09-25 23:25:34 +02:00
parent dc071b83f3
commit 1ea84f5b6e
4 changed files with 146 additions and 42 deletions
+31 -7
View File
@@ -221,15 +221,37 @@ let nTicks = states.len
let tfil = replayTfil(states, starts)
let sf = replayStrafe(states, starts)
# Field variant: the ring mover's retune, where no SINGLE soft source can poison
# a path (corridor below the threshold, wall radiance == the threshold). This is
# the same field shape the user's ring experiment used; we only test whether it
# unblocks the strafe line.
# ── reference fields for the Gate A comparison (j108's two rows) ───────────
# NOTE: `sf` above already ran with the CURRENT strafe defaults (the corrected
# retune: bullet 20/10, corridor 10, wall 15/5). The two replays below
# reproduce j108's historical rows so the numbers are comparable on one binary.
# j108 row 1: the SHIPPED TFIL field (bullet 10/5, corridor 20, wall 30/10).
StrafeBulletCore = 10.0
StrafeBulletAura = 5.0
StrafeCorridorHeat = 20.0
StrafeWallHotness = 30.0
StrafeWallRadiance = 10.0
let sfShipped = replayStrafe(states, starts)
# j108 row 2: the ring mover's retune field (bullet 10/5, corridor 5, wall 10/5).
StrafeBulletCore = 10.0
StrafeBulletAura = 5.0
StrafeCorridorHeat = 5.0
StrafeWallHotness = 10.0
StrafeWallRadiance = 5.0
let sfRetune = replayStrafe(states, starts)
loadStrafeHeatEnv() # restore the shipped field for any later use
# Isolation of DEFECT 2 alone: the ring retune's soft sources (corridor 5, wall
# 10/5) with ONLY the bullet heat raised to 20/10, so the gap between the
# corrected default and j108's retune row can be attributed.
StrafeBulletCore = 20.0
StrafeBulletAura = 10.0
StrafeCorridorHeat = 5.0
StrafeWallHotness = 10.0
StrafeWallRadiance = 5.0
let sfBulletOnly = replayStrafe(states, starts)
loadStrafeHeatEnv() # restore the CURRENT strafe defaults for any later use
echo "STRAFE gates — offline fixture ", fixtureRel, " (", nTicks, " ticks, ",
starts.len, " rounds), seed=", Seed
@@ -260,8 +282,10 @@ proc gateA(tag: string, rep: Replay) =
line.add &"{k}:{hist.getOrDefault(k,0)} "
echo " safe-count dist (0..12+) : ", line
gateA("shipped TFIL heat (corridor 20, wall 30/10)", sf)
gateA("ring retune (corridor 5, wall 10/5)", sfRetune)
gateA("STRAFE DEFAULT (corrected: bullet 20/10, corridor 10, wall 15/5)", sf)
gateA("j108 shipped TFIL field (bullet 10/5, corridor 20, wall 30/10)", sfShipped)
gateA("j108 ring retune (bullet 10/5, corridor 5, wall 10/5)", sfRetune)
gateA("bullet retune ISOLATED (bullet 20/10, corridor 5, wall 10/5)", sfBulletOnly)
echo ""
# ── GATE B ───────────────────────────────────────────────────────────────────