New mover `the_floor_is_lava_ring.nim`, a COPY of `the_floor_is_lava.nim` (which
stays byte-identical - the user explicitly wants the current TFIL preserved).
Selected only via `TR_MOVEMENT=tfil_ring`; the default stays `tfil`.
WHY: our measured real hit rate vs DrussGT is strongly range-dependent - 21.6%
at 0-100px, 27.1% at 100-200px, 19.3% at 200-300, 10.9% at 300-400, 6.8% at
400-600, 5.4% at 600-800 - but we shoot from ~450px on average. Plain TFIL has
no range preference at all.
THE ONE CHANGE: the final tile draw is re-weighted toward a target band.
rangeW(d) = 1.0 if lo<=d<=hi; exp(-((lo-d)/K)^2) if d<lo; exp(-((d-hi)/K)^2) if d>hi
w_i = rangeW(d_i)^(1/T); chosen ~ Categorical(w)
FLAT TOP on purpose: a Gaussian centred on the band midpoint would collapse the
band to a point and destroy the within-band hedge. `T` is the only knob;
`TR_TFIL_RANGE_TEMP=0` gives plain `rand(candidates.high)` - the exact control
arm. Safety stays a HARD constraint: the weighting only reorders the draw among
the pool the old code already accepted, so it can never pick a tile the old code
rejected (monotone refinement). Small pools (<4) stay uniform.
Randomness is deliberately KEPT: a measured A/B showed committing to the "best"
tile made real hit rate WORSE (7.02% -> 5.10%), so the distribution is tilted,
never removed.
HEAT TAMING (ring copy only; env-overridable):
TR_TFIL_CORRIDOR_HEAT 20.0 -> 5.0
TR_TFIL_WALL_HOTNESS 30.0 -> 10.0
Rationale, measured: `CorridorHeat=20` is TWICE `PathDangerThreshold=10`, so a
single corridor could poison a path by itself; `WallHotness=30` with
`WallRadiance=10` put the outer two tile rings over threshold on their own.
Per-source shares of total lava: wall 60.6%, corridor 25.4%, pillar 7.4%,
everything else <3%.
MEASURED EFFECT (primary fixture, 20,026 ticks / 15 rounds, field identity
verified max diff 0.000e+00):
metric original(20/30) ring(5/10)
band-weightable ticks 10.79% 26.45%
mean safeTiles/tick 15.19 85.04
ticks with 0 safe (pre-fallback) 58.5% 7.5%
safePool >= 4 39.05% 92.47%
>=1 safe tile in 100-200px 11.84% 26.64%
MEAN CLOSEST-SAFE-TILE DISTANCE 397.78px 284.84px
tiles > 10 threshold 0.61 0.15
The 397.78px figure is why the bot stayed far away: the safety filter left
nothing safe near the target, and 397px is our WORST range. Control: setting
corridor=20 wall=30 reproduces the original baseline exactly.
CEILING, honestly: even at corridor 0 / wall 0 only ~40% of ticks are
band-weightable, so no constant tweak fully unlocks the range weighting.
Ram unification: the ring mover takes a `band` field; ramming becomes just
`band=(0,50)`, so there is one movement engine. The `tfil` path is unchanged.
Observability: magenta annulus at the band edges, candidates tinted by weight,
chosen tile marked; one `[tfil_ring]` log line on change (now including
corridorHeat/wallHotness).
Guards: test_tfil_ring_weights 24/24 (new, pure, no battle), test_gun_harness 39,
test_vbullet_metric 11, test_power_selection 3, test_adaptive_radar 41.
UNVERIFIED: the mover's live effect. It has not been run in a battle yet.
- 30-tick cooldown after ghost-stuck/timeout ram exit prevents re-entry loop
- enemy_tracker.update() skips dead bots to prevent same-tick scan resurrection
- TFIL graphics cleared when ramming is active movement
- [config] logs: white base with green-highlighted changes only
- [ram:enter] logs trigger reason and key values on false→true transition
- [death] and [target-invalid] logs retained for diagnostics
PhantomMeteor:
- Ram finisher: charge at enemy when <200px and their energy <10
- Ram opportunity: charge when <60px and we have >20 energy advantage
- Integrated gunheat tracker for 1-2 tick earlier wave detection
- Distance control: smooth linear ramp toward preferred engagement distance
- Phantom range expanded 150→250px to catch closer threats
WaveSurfer:
- Wall-aware dodge bin selection: penalize bins leading off-arena
- Dodge timing: predict future position 15 ticks ahead for safety
- Distance control: radial blend when outside deadband (350±50px)
- Wall escape: invert strafe if pushing further into wall, blend toward center
ModularBot:
- Wired KNN gun (purple/magenta)
- Shadows tracked for movement (safer GF prediction)
- Bullet lifecycle management (onBulletFired/onBulletHitBot/onBulletHitWall)
- Unified phantom_meteor movement (wave_surfer unplugged)
- Config logging on round start + gun switch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>