ed25ce29ad
Task j111. Two changes to the TR_MOVEMENT=strafe engine, both OFF the shipped tfil path; the binary default is still tfil. RANGE CONTROL (a hypothesis under test, no default changed elsewhere): the body is still pinned ~perpendicular to the threat, but the line is tilted by the range error: lineAngle = threat + 90 + appliedTilt, with the tilt zero inside +/-TR_STRAFE_RANGE_TOL around TR_STRAFE_RANGE (200 px, chosen because it is exactly TR_POWER_FAR_DIST) and clamped to +/-TR_STRAFE_TILT_MAX. A tilt alone cannot change range (the picker chooses both ends at random), so the picker also PREFERS the end that reduces |distance - target| with a probability that grows with |tilt|; both ends stay possible. The tilt sign is aligned to the ENEMY bearing, since is the bullet direction (roughly its opposite) when a bullet is in flight. Knobs: TR_STRAFE_RANGE (200), TR_STRAFE_RANGE_TOL (25), TR_STRAFE_TILT_MAX (15), TR_STRAFE_TILT_GAIN (0.10), all registered in env_report.nim (emit + knownEnvNames). NOT claimed to be better: j107 measured that drifting 25-30 px closer made damage/run and wins WORSE. CORNER STALL (a real defect): a line whose in-arena candidate set was empty set targetValid=false and kept driving on the last sign, so the bot could oscillate inside a corner tile forever. Three defenses: (1) a deterministic corner guard projects the outward component off the line whenever BOTH ends are outside, so the line becomes wall-parallel and a candidate always exists; (2) a degenerate line (<=1 candidate) falls back to a radial search for the coolest in-arena tile and commits the sign; (3) a commanded move with no displacement for StuckFlipTicks (5) ticks flips the sign. Both warnings now reach the [strafe] log. GUI/log: the tilt is drawn as the existing strafe line (it is lineForward), plus a green/red ray toward the enemy (length = |distance-target|) and white text d=.. tgt=.. tilt=..; a red disc marks a stuck tick. The existing overlays and the j110 heat grid are unchanged. Gates (offline, kinematic replay of the DrussGT fixtures; see common_libs/tests/measure_strafe_range_stall.nim): on the j110 field all four corners that were 100% confined inside 72 px / 22.6 px max before now escape (<=2.6% confined, 209-741 px); the achieved |distance-200| falls on 3 of 4 fixtures (mean -16% to -30%); mean |turnRate| and the 8.00 px/tick speed are essentially unchanged (no-turn property survives). Reversal-interval entropy falls 5.85 -> 5.31 bits (still above TFIL's 5.09): the range bias costs some reversal randomness while closing.