j129: virtual-bullet debug overlay (TR_VBULLET_DEBUG, default off) - travelled path, predicted aim ring, hit/miss vector; shared turret colour table

This commit is contained in:
2026-09-26 11:02:39 +02:00
parent a5c893ace6
commit 752d3a3829
5 changed files with 480 additions and 19 deletions
+35 -19
View File
@@ -43,6 +43,7 @@ import movement_harness/bullet_shadows
import targeting/enemy_tracker
import targeting/target_selector
import env_report
import vbullet_draw
const botJsonPath = currentSourcePath().parentDir / "ModularBot.json"
const DebugVBullets = false
@@ -160,6 +161,17 @@ const TmHorizonId = 15
## head — hence every other gun's learning order — is byte-for-byte unchanged.
const BitBrainId = 16
## ── virtual-bullet debug overlay (TR_VBULLET_DEBUG*) ───────────────────────
## OFF by default: the shipped bot draws nothing. When on, the tracker's
## virtual bullets are overlaid so the training signal the selector learns
## from is visible (travelled path, predicted aim point, miss vector). The
## colour table is shared with the turret (see `vbullet_draw.gunColors`).
## Default filter: ONLY the currently selected gun, to stay legible; set
## `TR_VBULLET_DEBUG_GUN=all` or name a gun (e.g. `Pattern`).
let VBulletDebugOn = vBulletDebugEnabled()
let VBulletDebugMax = vBulletDebugMaxBullets()
let VBulletDebugGun = resolveGunFilter(getEnv(VBulletDebugGunEnv, ""), GunNames)
const
CLR_GUN = "\e[33m" # yellow
CLR_MOVE = "\e[36m" # cyan
@@ -1267,6 +1279,11 @@ method run*(bot: ModularBot) =
for id, es in bot.enemyTracker.enemies:
enemyPositions[id] = (x: es.x, y: es.y, lastSeenTick: es.lastSeenTick, alive: es.alive)
# Debug-only: bullets that resolve during `tickBullets` below are cleared
# from the tracker in the same tick, so capture their outcome here for the
# overlay's miss vector. Empty (and unused) when the overlay is off.
var resolvedMarks: seq[VbResolved]
let st = bot.lastState
bot.tracker.tickBullets(st, enemyPositions, proc(gunId: GunId, binIdx: int, fe: FeedbackEvent) =
case gunId
@@ -1294,6 +1311,10 @@ method run*(bot: ModularBot) =
of BitBrainId: bot.bitbrain.onResult(fe)
else: discard
if fe.hit: inc bot.virtualHits else: inc bot.virtualMiss
if VBulletDebugOn:
resolvedMarks.add VbResolved(
gunId: gunId, aimX: fe.prediction.x, aimY: fe.prediction.y,
actualX: fe.actualX, actualY: fe.actualY, hit: fe.hit)
when DebugVBullets:
let total = bot.virtualHits + bot.virtualMiss
let pct = if total > 0: bot.virtualHits.float / total.float * 100.0 else: 0.0
@@ -1320,25 +1341,11 @@ method run*(bot: ModularBot) =
if selectedGun != bot.currentGun:
bot.currentGun = selectedGun
bot.cfgDirty = true # gun switch: emit the config line at tick end
case selectedGun
of 0: setTurretColor("#FFFFFF"); setBulletColor("#FFFFFF") # HEADON: pure white (was #FF3333 red)
of 1: setTurretColor("#3366FF"); setBulletColor("#6699FF")
of 2: setTurretColor("#9933FF"); setBulletColor("#CC66FF")
of 3: setTurretColor("#33CC33"); setBulletColor("#66FF66")
of 4: setTurretColor("#FFCC00"); setBulletColor("#FFE066")
of 5: setTurretColor("#00CC44"); setBulletColor("#66FF99") # PATTERN: green (was #FF6600 orange, too close to BitBrain's pink)
of 6: setTurretColor("#CCCCCC"); setBulletColor("#EEEEEE")
of 7: setTurretColor("#FF00FF"); setBulletColor("#FF66FF")
of 8: setTurretColor("#990000"); setBulletColor("#CC3333")
of 9: setTurretColor("#006600"); setBulletColor("#009900")
of 10: setTurretColor("#996633"); setBulletColor("#CC9966")
of 11: setTurretColor("#008888"); setBulletColor("#00AAAA")
of 12: setTurretColor("#CC00CC"); setBulletColor("#FF44FF")
of 13: setTurretColor("#00FFCC"); setBulletColor("#66FFDD")
of TmPatternId: setTurretColor("#AAFF00"); setBulletColor("#CCFF66")
of TmHorizonId: setTurretColor("#00AAFF"); setBulletColor("#66CCFF")
of BitBrainId: setTurretColor("#FF1493"); setBulletColor("#FF69B4")
else: discard
# Per-gun colours live in `vbullet_draw.gunColors`, shared with the
# virtual-bullet overlay so turret and overlay can never drift apart.
let (turretColor, bulletColor) = gunColors(selectedGun)
setTurretColor(turretColor)
setBulletColor(bulletColor)
let pred = case selectedGun
of 1: bot.linear.predict(bot.lastState, bulletSpeed(power))
@@ -1387,6 +1394,12 @@ method run*(bot: ModularBot) =
setGunTurnRate(normDelta)
# Virtual-bullet overlay (default off). Drawn AFTER selection so the
# default "selected gun only" filter uses this tick's choice.
if VBulletDebugOn:
discard drawVirtualBullets(bot.tracker, resolvedMarks, selectedGun,
VBulletDebugGun, VBulletDebugMax, GunNames)
# Emit exactly one config line per tick, at the END of the tick, so the gun
# field reflects the selection made above rather than the previous tick's.
if bot.cfgDirty:
@@ -1449,6 +1462,9 @@ when isMainModule:
printEnvReport(EnvReportContext(
movementName: MovementName,
vBulletAdmitOnly: VBulletAdmitOnly,
vBulletDebug: VBulletDebugOn,
vBulletDebugGun: getEnv(VBulletDebugGunEnv, ""),
vBulletDebugMax: VBulletDebugMax,
recordWorldState: RecordWorldState,
radarForceSpin: RadarForceSpin,
radarScanLog: RadarScanLog,