j146 TFIL: the bullet's own heat becomes tunable (TR_TFIL_BULLET_CORE/AURA, default 10/5 = today) and the field-shape A/B is pre-registered
This commit is contained in:
@@ -107,6 +107,8 @@ TR_TFIL_RANGE_K=60.0 # px; how fast the weight falls off outside the band
|
||||
TR_TFIL_CORRIDOR_HEAT=10.0 # lava painted per corridor-overlapping tile
|
||||
TR_TFIL_WALL_HOTNESS=15.0 # peak heat painted on tiles next to a wall
|
||||
TR_TFIL_WALL_RADIANCE=10.0 # how fast wall heat falls off with distance
|
||||
TR_TFIL_BULLET_CORE=10.0 # tfil only: lava per bullet-overlapping tile (== PathDangerThreshold, so a bullet is never hot on its own)
|
||||
TR_TFIL_BULLET_AURA=5.0 # tfil only: lava for the bullet's aura ring tiles
|
||||
TR_TFIL_TILE_REPLAN=self # self | enemy | off: when a dodge commitment is cancelled
|
||||
TR_TFIL_COMMIT_TICKS=15 # ticks to commit to a dodge point before replanning
|
||||
TR_TFIL_NO_REV=off # on = never reverse direction inside a corridor
|
||||
|
||||
@@ -314,6 +314,10 @@ proc printEffectiveValues(ctx: EnvReportContext) =
|
||||
sourceOf("TR_TFIL_WALL_HOTNESS"))
|
||||
emit("TR_TFIL_WALL_RADIANCE", $the_floor_is_lava.WallRadiance,
|
||||
sourceOf("TR_TFIL_WALL_RADIANCE"))
|
||||
emit("TR_TFIL_BULLET_CORE", $the_floor_is_lava.BulletCore,
|
||||
sourceOf("TR_TFIL_BULLET_CORE"))
|
||||
emit("TR_TFIL_BULLET_AURA", $the_floor_is_lava.BulletAura,
|
||||
sourceOf("TR_TFIL_BULLET_AURA"))
|
||||
emit("TR_TFIL_TILE_REPLAN", tileReplanName(TfilTileReplanMode),
|
||||
sourceOf("TR_TFIL_TILE_REPLAN"))
|
||||
emit("TR_TFIL_COMMIT_TICKS", $TfilCommitTicks, sourceOf("TR_TFIL_COMMIT_TICKS"))
|
||||
@@ -638,7 +642,7 @@ proc knownEnvNames*(): seq[string] =
|
||||
"TR_RAM_PLAN_MARGIN", "TR_RAM_PLAN_HITRATE", "TR_RAM_LOG",
|
||||
"TR_TFIL_RANGE_LO", "TR_TFIL_RANGE_HI", "TR_TFIL_RANGE_TEMP",
|
||||
"TR_TFIL_RANGE_K", "TR_TFIL_CORRIDOR_HEAT", "TR_TFIL_WALL_HOTNESS",
|
||||
"TR_TFIL_WALL_RADIANCE",
|
||||
"TR_TFIL_WALL_RADIANCE", "TR_TFIL_BULLET_CORE", "TR_TFIL_BULLET_AURA",
|
||||
"TR_TFIL_TILE_REPLAN", "TR_TFIL_COMMIT_TICKS", "TR_TFIL_NO_REV",
|
||||
"TR_TFIL_COMMIT_LOG", "TR_TFIL_COMMIT_ARRIVAL", "TR_TFIL_COMMIT_MARGIN",
|
||||
"TR_TFIL_NOREV_SPEED", "TR_TFIL_TURN_BIAS", "TR_TFIL_TURN_REF_DEG",
|
||||
|
||||
@@ -18,8 +18,19 @@ const BulletCoreRadiusMax = 54.0 ## core radius at power 3.0
|
||||
const BulletAuraExtMin = 36.0 ## aura extension at power 3.0 (slow)
|
||||
const BulletAuraExtMax = 54.0 ## aura extension at power 0.1 (fast)
|
||||
|
||||
const BulletCore = 10.0 ## lava accumulation per bullet-overlapping tile
|
||||
const BulletAura = 5.0 ## lava accumulation for aura ring tiles
|
||||
## j146: the bullet's OWN heat was a Nim `const`, so no env arm could move it
|
||||
## and 10.0 is exactly `PathDangerThreshold` — a bullet is never dangerous on
|
||||
## its own and the corridor carries the whole field. Same env-overridable-var
|
||||
## pattern as `CorridorHeat` below; the DEFAULTS are today's `const`s, so the
|
||||
## default field is bit-identical.
|
||||
## TR_TFIL_BULLET_CORE default 10.0 lava per bullet-overlapping tile
|
||||
## TR_TFIL_BULLET_AURA default 5.0 lava for aura ring tiles
|
||||
const
|
||||
DefaultBulletCore = 10.0
|
||||
DefaultBulletAura = 5.0
|
||||
var
|
||||
BulletCore* = DefaultBulletCore
|
||||
BulletAura* = DefaultBulletAura
|
||||
|
||||
const EnemyCoreRadius = 18.0 ## half of 36px body
|
||||
const EnemyAuraRadius = 54.0 ## 18 + 36
|
||||
@@ -201,6 +212,8 @@ proc loadTfilHeatShapeEnv*() =
|
||||
CorridorHeat = max(0.0, getEnvFloat("TR_TFIL_CORRIDOR_HEAT", DefaultCorridorHeat))
|
||||
WallHotness = max(0.0, getEnvFloat("TR_TFIL_WALL_HOTNESS", DefaultWallHotness))
|
||||
WallRadiance = max(0.0, getEnvFloat("TR_TFIL_WALL_RADIANCE", DefaultWallRadiance))
|
||||
BulletCore = max(0.0, getEnvFloat("TR_TFIL_BULLET_CORE", DefaultBulletCore))
|
||||
BulletAura = max(0.0, getEnvFloat("TR_TFIL_BULLET_AURA", DefaultBulletAura))
|
||||
|
||||
loadTfilHeatShapeEnv()
|
||||
|
||||
@@ -307,6 +320,10 @@ type
|
||||
## candidate set of the last pick (j145:
|
||||
## lets a caller measure the REGRET of the
|
||||
## draw instead of only the drawn value)
|
||||
lastPickSafe: int ## how many SAFE tiles (pathMaxHeat
|
||||
## <= PathDangerThreshold) the last pick
|
||||
## drew from (j146: the size of the set
|
||||
## the heat SHAPE leaves available)
|
||||
|
||||
proc initTFIL*(): TFILModule = TFILModule(debugGraphics: false, fire: initFireTracker())
|
||||
|
||||
@@ -352,6 +369,7 @@ proc resetRound*(m: var TFILModule) =
|
||||
m.picks = 0
|
||||
m.lastPickPromoted = false
|
||||
m.lastPickMinTurn = 0.0
|
||||
m.lastPickSafe = 0
|
||||
|
||||
# ── Commit diagnostics (TR_TFIL_COMMIT_LOG, off by default) ──────────────────
|
||||
# One JSONL line per computeMove call, used by the A/B to prove the treatment
|
||||
@@ -1119,6 +1137,7 @@ proc computeMove*(m: var TFILModule, ws: WorldState): MoveCommand =
|
||||
var minTurn = Inf
|
||||
for t in candidates: minTurn = min(minTurn, t.turnDeg)
|
||||
m.lastPickMinTurn = if minTurn == Inf: ct.turnDeg else: minTurn
|
||||
m.lastPickSafe = candidates.len
|
||||
m.commitTarget = (x: m.marginX + (ct.col.float + 0.5) * GridSize,
|
||||
y: m.marginY + (ct.row.float + 0.5) * GridSize)
|
||||
m.commitTicks = TfilCommitTicks
|
||||
|
||||
@@ -3218,3 +3218,96 @@ nothing.
|
||||
**The shipped default is untouched.** `TR_MOVEMENT=strafe` remains the default
|
||||
(`ModularBot_garage/src/ModularBot.nim:129`); both new knobs default to
|
||||
off-effect, so `TR_MOVEMENT=tfil` still means today's tfil, byte-for-byte.
|
||||
|
||||
---
|
||||
|
||||
# Batch 7 — TFIL field shape (j146)
|
||||
|
||||
*Pre-registered BEFORE any battle of this batch was launched. No battle of this
|
||||
batch existed when this section was written; the frozen binary for it is the
|
||||
commit that adds the two bullet-heat knobs.*
|
||||
|
||||
## The cause this batch fixes
|
||||
|
||||
Two consecutive tfil fixes (j144 `d2005ab` arrival + no-rev, j145 `39c90fd`
|
||||
turn-bias) improved the MECHANISM and the live outcome stayed an under-powered
|
||||
null. j145's offline gate then found the UPSTREAM cause, on the same fixture:
|
||||
|
||||
1. **58.8% of tfil's picks have no safe set to tie-break in** — fewer than two
|
||||
reachable tiles under `PathDangerThreshold = 10`.
|
||||
2. `TR_TFIL_CORRIDOR_HEAT = 20` is **twice** that threshold, so ONE far bullet's
|
||||
corridor marks a wide swath unsafe by itself; j145 measured `filter broken` on
|
||||
**59%** of picks.
|
||||
3. In tfil **the bullet's own heat is inert**: `BulletCore = 10.0` /
|
||||
`BulletAura = 5.0` were Nim `const`s, and 10 is exactly the threshold, so a
|
||||
bullet is never dangerous on its own and the corridor carries all the weight.
|
||||
|
||||
The SHAPE was already measured — but on **strafe**, in j119 batch 4, where the
|
||||
engine and the field differ. `field_strong` (corridor 20, wall 30/10, i.e. exactly
|
||||
what tfil ships) was **-0.29 wins/run, p=0.039**; `field_off` was **-0.47
|
||||
wins/run, p=0.0063**; the reference strafe shape — bullet core 20 / aura 10,
|
||||
corridor 10, wall 15 / radiance 5 — was the champion. **Both extremes lose and
|
||||
the middle wins, and tfil has never been run on the middle shape.**
|
||||
|
||||
## The treatment (Task A)
|
||||
|
||||
`BulletCore` / `BulletAura` in `the_floor_is_lava.nim` go from `const` to
|
||||
env-overridable `var`s, the exact pattern j119/j134 already used for
|
||||
`CorridorHeat` / `WallHotness` / `WallRadiance`:
|
||||
|
||||
| knob | default | meaning |
|
||||
|---|---|---|
|
||||
| `TR_TFIL_BULLET_CORE` | `10.0` (= today's `const`) | lava per bullet-overlapping tile |
|
||||
| `TR_TFIL_BULLET_AURA` | `5.0` (= today's `const`) | lava for the bullet's aura ring |
|
||||
|
||||
**Default-off-effect**: the defaults are today's `const`s, so the default path is
|
||||
byte-for-byte unchanged and the golden in `test_tfil_commit_env.nim` is not
|
||||
regenerated.
|
||||
|
||||
## Arms (frozen, all `TR_MOVEMENT=tfil`, j144 ON in every arm)
|
||||
|
||||
j144's recommended `.env` (`TR_TFIL_COMMIT_ARRIVAL=1 TR_TFIL_NOREV_SPEED=4`) is
|
||||
ON in **every** arm, so the shape is isolated **on top of the current best tfil**.
|
||||
The virtual pillar is OFF (shipped 0/0) in every arm. Columns are
|
||||
corridor / wall hotness / wall radiance / bullet core / bullet aura.
|
||||
|
||||
| # | arm | shape | what it isolates |
|
||||
|---|---|---|---|
|
||||
| 1 | `shape_shipped` | 20/30/10/10/5 | **the reference** — today's field, on j144 |
|
||||
| 2 | `shape_middle` | 10/15/5/20/10 | the shape strafe won on |
|
||||
| 3 | `shape_corr10` | 10/30/10/10/5 | "corridor alone was the problem" |
|
||||
| 4 | `shape_bullets` | 20/30/10/20/10 | "bullets made dangerous themselves" |
|
||||
| 5 | `shape_nofield` | 0/0/0/20/10 | the "field off" control strafe measured as WORSE |
|
||||
|
||||
`shape_nofield` is here so a `shape_middle` win cannot be a FALSE WINNER: it
|
||||
separates "the middle shape is right" from "any less lava is right". Note that
|
||||
`TR_TFIL_WALL_HOTNESS=0` is what zeroes the wall (radiance 0 would paint a FLAT
|
||||
field over the whole arena — the opposite of "no walls"). The enemy body/aura
|
||||
heat (`EnemyCore 40` / `EnemyAura 10`) is a `const` with no knob and is present in
|
||||
every arm; "field off" therefore means no **corridor/wall** field.
|
||||
|
||||
Panel: the FROZEN 15-opponent `tools/ab/panel_movement.txt`. Harness:
|
||||
`tools/ab/tournament_run.sh` + `tournament_analyze.py`. Arms file
|
||||
`tools/ab/arms_tfil_shape.txt`.
|
||||
|
||||
## Pre-registered prediction and decision rule
|
||||
|
||||
* **Prediction.** `shape_middle` > `shape_shipped` on damage/run and round wins
|
||||
(it is the shape strafe measured as best, and it is the only arm that both
|
||||
halves the corridor AND makes the bullet itself dangerous, which is what
|
||||
should restore a real safe set). `shape_corr10` and `shape_bullets` should
|
||||
each move part of the way. `shape_nofield` should be the WEAKEST arm, matching
|
||||
strafe's `field_off` — if `shape_nofield` ties `shape_middle` on the primary
|
||||
metrics, the win is "less lava", not "this shape", and that is recorded as a
|
||||
wrong prediction.
|
||||
* **Mechanism, not verdict.** The **offline `filter broken` rate and the mean
|
||||
safe-candidate count** are the mechanism. The **incoming hit rate** is the
|
||||
live mechanism. The **verdict is damage/run and round wins** under the
|
||||
campaign's pre-registered rule 2: the cross-opponent sign test p < 0.05 on one
|
||||
primary metric with the other not down, with SD/SE/95% CI/MDE reported.
|
||||
* **If nothing separates**, the verdict is *not distinguishable* and the shape is
|
||||
**not changed**. The pre-registered bar is not re-interpreted afterwards.
|
||||
* **A null does NOT retract j144's mechanism result** and does not change the
|
||||
shipped `TR_MOVEMENT=strafe` default.
|
||||
|
||||
*(results appended below after the battles)*
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# arms_tfil_shape.txt — j146: the TFIL FIELD SHAPE, five arms on the frozen
|
||||
# movement panel (tools/ab/panel_movement.txt), all with TR_MOVEMENT=tfil pinned
|
||||
# EXPLICITLY and the j144 fixes ON, so only the SHAPE varies.
|
||||
#
|
||||
# WHY. The strafe sweep (j119 batch 4) already measured the field SHAPE, not the
|
||||
# engine: the shape tfil SHIPS (corridor 20, wall 30/10) was -0.29 wins/run
|
||||
# (p=0.039) and field_off was -0.47 wins/run (p=0.0063); the champion was the
|
||||
# MIDDLE shape (bullet core 20 / aura 10, corridor 10, wall 15 / radiance 5).
|
||||
# tfil has never been run on the middle shape. On top of that, j145 found 59% of
|
||||
# tfil's picks have no safe set to tie-break in, because corridor 20 is TWICE
|
||||
# PathDangerThreshold 10 and tfil's own bullet heat (core 10) is exactly the
|
||||
# threshold, so a bullet is never dangerous by itself.
|
||||
#
|
||||
# Every arm carries `TR_TFIL_COMMIT_ARRIVAL=1 TR_TFIL_NOREV_SPEED=4` (j144's
|
||||
# recommended .env) so the shape is isolated ON TOP OF THE CURRENT BEST TFIL.
|
||||
# The pillar stays OFF (shipped default 0/0) in every arm.
|
||||
#
|
||||
# Columns: CORRIDOR / WALL_HOTNESS / WALL_RADIANCE / BULLET_CORE / BULLET_AURA
|
||||
#
|
||||
# Pre-registered in docs/movement_campaign.md ("Batch 7 — TFIL field shape")
|
||||
# BEFORE any of these battles ran. Reference is `shape_shipped`.
|
||||
#
|
||||
# Format: name | ENV=value ENV=value | label
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
# 1. THE ARM TO BEAT — the field tfil ships today (20/30/10/10/5), on j144.
|
||||
shape_shipped | TR_MOVEMENT=tfil TR_TFIL_COMMIT_ARRIVAL=1 TR_TFIL_NOREV_SPEED=4 TR_TFIL_CORRIDOR_HEAT=20 TR_TFIL_WALL_HOTNESS=30 TR_TFIL_WALL_RADIANCE=10 TR_TFIL_BULLET_CORE=10 TR_TFIL_BULLET_AURA=5 | today's shape, on j144 (control / reference)
|
||||
|
||||
# 2. THE CANDIDATE — the shape strafe measured as the champion.
|
||||
shape_middle | TR_MOVEMENT=tfil TR_TFIL_COMMIT_ARRIVAL=1 TR_TFIL_NOREV_SPEED=4 TR_TFIL_CORRIDOR_HEAT=10 TR_TFIL_WALL_HOTNESS=15 TR_TFIL_WALL_RADIANCE=5 TR_TFIL_BULLET_CORE=20 TR_TFIL_BULLET_AURA=10 | the middle shape strafe won on (10/15/5/20/10)
|
||||
|
||||
# 3. "corridor alone was the problem" — only the corridor is halved.
|
||||
shape_corr10 | TR_MOVEMENT=tfil TR_TFIL_COMMIT_ARRIVAL=1 TR_TFIL_NOREV_SPEED=4 TR_TFIL_CORRIDOR_HEAT=10 TR_TFIL_WALL_HOTNESS=30 TR_TFIL_WALL_RADIANCE=10 TR_TFIL_BULLET_CORE=10 TR_TFIL_BULLET_AURA=5 | shipped, corridor 20->10 only
|
||||
|
||||
# 4. "bullets made dangerous themselves" — only the bullet's own heat is raised.
|
||||
shape_bullets | TR_MOVEMENT=tfil TR_TFIL_COMMIT_ARRIVAL=1 TR_TFIL_NOREV_SPEED=4 TR_TFIL_CORRIDOR_HEAT=20 TR_TFIL_WALL_HOTNESS=30 TR_TFIL_WALL_RADIANCE=10 TR_TFIL_BULLET_CORE=20 TR_TFIL_BULLET_AURA=10 | shipped, bullet core/aura 10/5 -> 20/10 only
|
||||
|
||||
# 5. THE "field off" CONTROL — corridors, walls and pillar all zero, so the
|
||||
# bullets' own heat is the only bullet threat. strafe measured this shape as
|
||||
# the WORST of the three; it is here so a middle-shape win cannot be a false
|
||||
# winner (it proves the win is the middle, not simply "less lava").
|
||||
shape_nofield | TR_MOVEMENT=tfil TR_TFIL_COMMIT_ARRIVAL=1 TR_TFIL_NOREV_SPEED=4 TR_TFIL_CORRIDOR_HEAT=0 TR_TFIL_WALL_HOTNESS=0 TR_TFIL_WALL_RADIANCE=0 TR_TFIL_BULLET_CORE=20 TR_TFIL_BULLET_AURA=10 | no corridor/wall field, bullets only (0/0/0/20/10)
|
||||
Reference in New Issue
Block a user