j140 rename the lead-gain corrector: BitBrain -> LEADGAIN (+ legacy TR_BITBRAIN_* aliases)
The gun at rack id 16 learned a multiplier for Pattern's lead, separately
per range band. It was called BITBRAIN and shipped a TR_BITBRAIN_* prefix,
which is why the name read as a neural network it no longer contains.
guns/bitbrain_gun.nim -> guns/lead_gain.nim (rack id 16 UNCHANGED)
RackGunNames[16] BITBRAIN -> LEADGAIN
TR_BITBRAIN_* knobs -> TR_LEADGAIN_*
[bb] log line -> [lg]
BACKWARD COMPATIBILITY is mandatory: the live .env carries
TR_RACK_BITBRAIN=both, TR_BITBRAIN_GAINS, TR_BITBRAIN_MEM=decay and
TR_BITBRAIN_LOG=1, and those must keep behaving identically. The new ADE+SBC
gun (next commit) claims the BITBRAIN name and the TR_BITBRAIN_* prefix, so
the namespace is disambiguated by ONE deterministic switch, TR_BITBRAIN_NET
(default 0):
TR_BITBRAIN_NET unset/0 -> LEGACY: the 14 frozen legacy suffixes are aliases
for TR_LEADGAIN_*, and TR_RACK_BITBRAIN still
selects rack id 16. One [depr] line on stderr
names the new spelling of each honoured knob.
TR_BITBRAIN_NET = 1 -> the TR_BITBRAIN_* names belong to the new gun.
The legacy suffix set and the new gun's knob set are DISJOINT, so no name is
ever claimed twice; the new name always wins over its alias.
Parity: shipped rack is still onlyPattern, shipped movement is still strafe.
Guards unchanged: test_env_report 25, test_rack_membership 48,
test_tm_pattern_registration 20, test_lead_gain_registration 13 (was
test_bitbrain_registration), test_bitbrain 56, test_gun_harness 39,
test_tfil_commit_env 30. New: test_lead_gain_legacy 24.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
## ModularBot — plugin gun architecture tracer bullet.
|
||||
## Guns: HeadOnGun (0), LinearGun (1), TsetlinGun (2), CircularGun (3), GFGun (4), PatternMatcherGun (5), WallBounceGun (6), AccelGun (7), StopShotGun (8), DisplacementGun (9), AveragedLeadGun (10), DecayGFGun (11), KNNGun (12), TmSelectorGun (13), TmPatternGun (14), TmHorizonGun (15), BitBrainGun (16) via GunHarness.
|
||||
## Guns: HeadOnGun (0), LinearGun (1), TsetlinGun (2), CircularGun (3), GFGun (4), PatternMatcherGun (5), WallBounceGun (6), AccelGun (7), StopShotGun (8), DisplacementGun (9), AveragedLeadGun (10), DecayGFGun (11), KNNGun (12), TmSelectorGun (13), TmPatternGun (14), TmHorizonGun (15), LeadGainGun (16) via GunHarness.
|
||||
## Radar: RadarLockModule (1v1) / AdaptiveMeleeRadarModule (2+ enemies), auto-switched per tick.
|
||||
## Movement: OscillatorModule (perpendicular strafing).
|
||||
|
||||
@@ -30,7 +30,7 @@ import guns/knn_gun
|
||||
import guns/tm_selector
|
||||
import guns/tm_pattern
|
||||
import guns/tm_horizon
|
||||
import guns/bitbrain_gun
|
||||
import guns/lead_gain
|
||||
import movements/phantom_meteor
|
||||
import movements/rammer
|
||||
import movements/ram_decision
|
||||
@@ -150,7 +150,7 @@ let ResultLog* =
|
||||
block:
|
||||
let v = getEnv("TR_RESULT_LOG", "1").strip().toLowerAscii()
|
||||
not (v == "0" or v == "false" or v == "no" or v == "off")
|
||||
const GunNames = ["HeadOn", "Linear", "Tsetlin", "Circular", "GuessFactor", "Pattern", "WallBounce", "Accel", "StopShot", "Displace", "AvgLead", "DecayGF", "KNN", "TMSelect", "TMPattern", "TMHorizon", "BitBrain"]
|
||||
const GunNames = ["HeadOn", "Linear", "Tsetlin", "Circular", "GuessFactor", "Pattern", "WallBounce", "Accel", "StopShot", "Displace", "AvgLead", "DecayGF", "KNN", "TMSelect", "TMPattern", "TMHorizon", "LeadGain"]
|
||||
|
||||
## Rack id of the new TM pattern gun. It defaults to `TR_RACK_TMPATTERN=off`;
|
||||
## unlike the other guns, its virtual-bullet spawn is gated on rack admission
|
||||
@@ -165,11 +165,14 @@ const TmPatternId = 14
|
||||
## head — hence every other gun's learning order — is byte-for-byte unchanged.
|
||||
const TmHorizonId = 15
|
||||
|
||||
## Rack id of the BitBrain ADE+SBC fine-grained aim corrector. It defaults to
|
||||
## `TR_RACK_BITBRAIN=off` and its virtual-bullet spawn is gated on rack
|
||||
## admission, so the shipped default never spawns it and the shared tracker ring
|
||||
## head — hence every other gun's learning order — is byte-for-byte unchanged.
|
||||
const BitBrainId = 16
|
||||
## Rack id of the LEADGAIN per-range-band lead-gain corrector (it used to be
|
||||
## called BitBrain, before the ADE+SBC network was removed from it; the rack id
|
||||
## is unchanged). It defaults to `TR_RACK_LEADGAIN=off` and its virtual-bullet
|
||||
## spawn is gated on rack admission, so the shipped default never spawns it and
|
||||
## the shared tracker ring head — hence every other gun's learning order — is
|
||||
## byte-for-byte unchanged. `TR_RACK_BITBRAIN` still selects it while
|
||||
## `TR_BITBRAIN_NET` is off (backward compatibility; see `gun_harness/selector`).
|
||||
const LeadGainId = 16
|
||||
|
||||
## ── virtual-bullet debug overlay (TR_VBULLET_DEBUG*) ───────────────────────
|
||||
## OFF by default: the shipped bot draws nothing. When on, the tracker's
|
||||
@@ -242,7 +245,7 @@ type
|
||||
tmSelector: TmSelectorGun
|
||||
tmPattern: TmPatternGun
|
||||
tmHorizon: TmHorizonGun
|
||||
bitbrain: BitBrainGun
|
||||
leadGain: LeadGainGun
|
||||
mover: TFILModule
|
||||
ringMover: TFILRingModule
|
||||
strafeMover: StrafeModule
|
||||
@@ -937,11 +940,11 @@ method onRoundStarted*(bot: ModularBot, e: RoundStartedEvent) =
|
||||
bot.tmHorizon.resetRoundState()
|
||||
if e.roundNumber <= 1:
|
||||
bot.tmHorizon.resetLearning("round1")
|
||||
# BitBrain: per-round wipe always clears observations/labels; `perRound` mode
|
||||
# LEADGAIN: per-round wipe always clears observations/labels; `perRound` mode
|
||||
# also wipes its SBCs (the gate test's winning regime).
|
||||
bot.bitbrain.resetRoundState()
|
||||
bot.leadGain.resetRoundState()
|
||||
if e.roundNumber <= 1:
|
||||
bot.bitbrain.resetLearning("round1")
|
||||
bot.leadGain.resetLearning("round1")
|
||||
bot.isRamming = false
|
||||
bot.ramDurationTicks = 0
|
||||
bot.ramCooldownTicks = 0
|
||||
@@ -1021,7 +1024,7 @@ method onGameStarted*(bot: ModularBot, e: GameStartedEventForBot) =
|
||||
# state, so this is the only place the learning is dropped at a battle
|
||||
# boundary. The round-1 fallback in `onRoundStarted` covers a missed callback.
|
||||
bot.tmHorizon.resetLearning("game_start")
|
||||
bot.bitbrain.resetLearning("game_start")
|
||||
bot.leadGain.resetLearning("game_start")
|
||||
# minNumberOfParticipants == maxNumberOfParticipants for fixed battles; self is -1
|
||||
bot.initialEnemyCount = e.gameSetup.minNumberOfParticipants - 1
|
||||
# New battle: reset the cumulative outcome counters and remember how many
|
||||
@@ -1129,7 +1132,7 @@ method run*(bot: ModularBot) =
|
||||
# target changes to a different bot id (gated by
|
||||
# TR_TMHORIZON_RESET_ON_TARGET, default on; no-op in 1v1).
|
||||
discard bot.tmHorizon.targetChanged(candidateId)
|
||||
discard bot.bitbrain.targetChanged(candidateId)
|
||||
discard bot.leadGain.targetChanged(candidateId)
|
||||
if bot.currentTargetId >= 0:
|
||||
bot.lastKnownTargetId = bot.currentTargetId
|
||||
|
||||
@@ -1273,7 +1276,7 @@ method run*(bot: ModularBot) =
|
||||
var tmselPreds: array[len(PowerBins), GunPrediction]
|
||||
var tmpPreds: array[len(PowerBins), GunPrediction]
|
||||
var tmhPreds: array[len(PowerBins), GunPrediction]
|
||||
var bbPreds: array[len(PowerBins), GunPrediction]
|
||||
var lgPreds: array[len(PowerBins), GunPrediction]
|
||||
# ── TR_VBULLET_ADMIT_ONLY gate ──────────────────────────────────────
|
||||
# Rack membership used to filter only SELECTION, so every unselected gun
|
||||
# still ran predict()+spawnBullets() each tick to feed a fitness table the
|
||||
@@ -1290,7 +1293,7 @@ method run*(bot: ModularBot) =
|
||||
admit[gi] = ModuleVBullets and
|
||||
vBulletAdmitted(gi, bot.rackMode, ActiveRackMembership,
|
||||
VBulletAdmitOnly or gi == TmPatternId or
|
||||
gi == TmHorizonId or gi == BitBrainId)
|
||||
gi == TmHorizonId or gi == LeadGainId)
|
||||
for i in 0..<len(PowerBins):
|
||||
if admit[0]: headsUp[i] = bot.headOn.predict(bot.lastState, bulletSpeed(PowerBins[i]))
|
||||
if admit[1]: linPreds[i] = bot.linear.predict(bot.lastState, bulletSpeed(PowerBins[i]))
|
||||
@@ -1313,9 +1316,9 @@ method run*(bot: ModularBot) =
|
||||
# TMHORIZON likewise: Pattern base + horizon TM correction.
|
||||
if admit[TmHorizonId]:
|
||||
tmhPreds[i] = bot.tmHorizon.predict(bot.lastState, bulletSpeed(PowerBins[i]))
|
||||
# BITBRAIN: Pattern base + ADE/SBC fine-grained angular correction.
|
||||
if admit[BitBrainId]:
|
||||
bbPreds[i] = bot.bitbrain.predict(bot.lastState, bulletSpeed(PowerBins[i]))
|
||||
# LEADGAIN: Pattern base, lead scaled by a per-band learned gain.
|
||||
if admit[LeadGainId]:
|
||||
lgPreds[i] = bot.leadGain.predict(bot.lastState, bulletSpeed(PowerBins[i]))
|
||||
|
||||
if admit[0]: bot.tracker.spawnBullets(0, headsUp, bot.lastState, tid)
|
||||
if admit[1] and not gunDisabled(1): bot.tracker.spawnBullets(1, linPreds, bot.lastState, tid)
|
||||
@@ -1337,8 +1340,8 @@ method run*(bot: ModularBot) =
|
||||
bot.tracker.spawnBullets(TmPatternId, tmpPreds, bot.lastState, tid)
|
||||
if admit[TmHorizonId] and not gunDisabled(TmHorizonId):
|
||||
bot.tracker.spawnBullets(TmHorizonId, tmhPreds, bot.lastState, tid)
|
||||
if admit[BitBrainId] and not gunDisabled(BitBrainId):
|
||||
bot.tracker.spawnBullets(BitBrainId, bbPreds, bot.lastState, tid)
|
||||
if admit[LeadGainId] and not gunDisabled(LeadGainId):
|
||||
bot.tracker.spawnBullets(LeadGainId, lgPreds, bot.lastState, tid)
|
||||
|
||||
# Build slim enemy table for tickBullets
|
||||
var enemyPositions: Table[int, tuple[x, y: float, lastSeenTick: int, alive: bool]]
|
||||
@@ -1374,7 +1377,7 @@ method run*(bot: ModularBot) =
|
||||
of 13: bot.tmSelector.onResult(fe)
|
||||
of TmPatternId: bot.tmPattern.onResult(fe)
|
||||
of TmHorizonId: bot.tmHorizon.onResult(fe)
|
||||
of BitBrainId: bot.bitbrain.onResult(fe)
|
||||
of LeadGainId: bot.leadGain.onResult(fe)
|
||||
else: discard
|
||||
if fe.hit: inc bot.virtualHits else: inc bot.virtualMiss
|
||||
if DebugDrawOn and VBulletDebugOn:
|
||||
@@ -1429,7 +1432,7 @@ method run*(bot: ModularBot) =
|
||||
of 13: bot.tmSelector.predict(bot.lastState, bulletSpeed(power))
|
||||
of TmPatternId: bot.tmPattern.predict(bot.lastState, bulletSpeed(power))
|
||||
of TmHorizonId: bot.tmHorizon.predict(bot.lastState, bulletSpeed(power))
|
||||
of BitBrainId: bot.bitbrain.predict(bot.lastState, bulletSpeed(power))
|
||||
of LeadGainId: bot.leadGain.predict(bot.lastState, bulletSpeed(power))
|
||||
else: bot.headOn.predict(bot.lastState, bulletSpeed(power))
|
||||
let aimTarget = aimAngle(getX(), getY(), pred.x, pred.y)
|
||||
|
||||
@@ -1497,7 +1500,7 @@ proc seedSelectorRng() =
|
||||
|
||||
when isMainModule:
|
||||
var bot = ModularBot(
|
||||
tracker: vb.initTracker(17), # 0: HeadOn, 1: Linear, 2: Tsetlin, 3: Circular, 4: GuessFactor, 5: Pattern, 6: WallBounce, 7: Accel, 8: StopShot, 9: Displace, 10: AvgLead, 11: DecayGF, 12: KNN, 13: TMSelect, 14: TMPattern, 15: TMHorizon, 16: BitBrain
|
||||
tracker: vb.initTracker(17), # 0: HeadOn, 1: Linear, 2: Tsetlin, 3: Circular, 4: GuessFactor, 5: Pattern, 6: WallBounce, 7: Accel, 8: StopShot, 9: Displace, 10: AvgLead, 11: DecayGF, 12: KNN, 13: TMSelect, 14: TMPattern, 15: TMHorizon, 16: LeadGain
|
||||
headOn: HeadOnGun(),
|
||||
linear: LinearGun(),
|
||||
circular: CircularGun(),
|
||||
@@ -1514,7 +1517,7 @@ when isMainModule:
|
||||
tmSelector: initTmSelectorGun(),
|
||||
tmPattern: initTmRadialGun(),
|
||||
tmHorizon: initTmHorizonGun(),
|
||||
bitbrain: initBitBrainGun(),
|
||||
leadGain: initLeadGainGun(),
|
||||
radar: RadarLockModule(),
|
||||
meleeRadar: initAdaptiveMeleeRadar(),
|
||||
mover: TFILModule(debugGraphics: DebugDrawOn),
|
||||
@@ -1553,6 +1556,6 @@ when isMainModule:
|
||||
disabledGuns: DisabledGuns,
|
||||
tmHorizon: bot.tmHorizon,
|
||||
patternMatcher: bot.patternMatcher,
|
||||
bitbrain: bot.bitbrain,
|
||||
leadGain: bot.leadGain,
|
||||
))
|
||||
start(bot, botJsonPath)
|
||||
|
||||
Reference in New Issue
Block a user