Files
SirRoboGarage/common_libs/tests/test_lead_gain_registration.nim
T
SirStone 7a6237ec20 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>
2026-09-26 15:54:00 +02:00

90 lines
3.4 KiB
Nim

## Default-parity + registration guard for the LEADGAIN gun (id 16).
##
## No Java, no battle, no network build. Covers:
## * the rack table carries LEADGAIN at id 16 and it defaults to `off`;
## * the shipped rack still admits exactly Pattern;
## * TR_RACK_LEADGAIN=both is what admits it, and the spawn gate honours it;
## (the LEGACY TR_RACK_BITBRAIN alias is pinned by
## `test_lead_gain_legacy.nim`);
## * `initLeadGainGun()` is LAZY (no learner) and does NOT touch the global
## RNG, so the default path cannot perturb the selector's random draws;
## * the mode parser defaults to `perRound`.
##
## Run: nim c -r common_libs/tests/test_lead_gain_registration.nim
import std/[random, os, math]
import gun_harness/virtual_bullets
import gun_harness/selector
import guns/lead_gain
const LeadGainId = 16
const PatternId = 5
var failures = 0
proc check(name: string, ok: bool) =
if ok: echo "PASS: ", name
else: echo "FAIL: ", name; inc failures
proc clearRackEnv() =
for name in RackGunNames: delEnv("TR_RACK_" & name)
proc testTable() =
check "rack: LEADGAIN is registered at id 16", RackGunNames[LeadGainId] == "LEADGAIN"
check "rack: LEADGAIN defaults to `off`", DefaultRackMembership[LeadGainId] == rmOff
var onlyPattern = true
for i in 0..<RackGunNames.len:
let want = if i == PatternId: rmBoth else: rmOff
if DefaultRackMembership[i] != want: onlyPattern = false
check "rack: the shipped default is still the onlyPattern rack", onlyPattern
check "rack: the default rack admits only Pattern (1v1)",
admittedGuns(17, rm1v1, DefaultRackMembership) == @[PatternId]
check "gate: LEADGAIN is NOT spawned under the default rack",
not vBulletAdmitted(LeadGainId, rm1v1, DefaultRackMembership, true)
proc testEnvOverride() =
clearRackEnv()
putEnv("TR_RACK_LEADGAIN", "both")
let m = loadRackMembership()
check "env: TR_RACK_LEADGAIN=both admits LEADGAIN",
m[LeadGainId] == rmBoth and
vBulletAdmitted(LeadGainId, rm1v1, m, true)
check "env: admitting LEADGAIN leaves Pattern as the only other member",
admittedGuns(17, rm1v1, m) == @[PatternId, LeadGainId]
clearRackEnv()
proc testLazyAndRngClean() =
delEnv("TR_LEADGAIN_MEM"); delEnv("TR_BITBRAIN_MEM")
var g = initLeadGainGun()
check "lazy: constructing the gun does NOT build the learner (0 bytes)",
g.networkBytes == 0
check "default: unset TR_LEADGAIN_MEM is perRound",
g.memMode == lgPerRound
check "parse: retained/decay/unknown",
parseLgMemMode("retained") == lgRetained and
parseLgMemMode("decay") == lgDecay and
parseLgMemMode("banana") == lgPerRound
# Global RNG parity: constructing the gun must not consume global randomness.
randomize(1234)
let a = rand(1_000_000)
randomize(1234)
var g2 = initLeadGainGun()
discard g2
let b = rand(1_000_000)
check "parity: initLeadGainGun() does not perturb the global RNG", a == b
proc testGeometry() =
check "geometry: class 0 centre is the low edge + half a bin",
abs(lgCenterDeg(0, 32, 40.0) - (-40.0 + 0.5 * 80.0 / 32.0)) < 1e-9
check "geometry: lgClassOf round-trips the centre",
lgClassOf(degToRad(lgCenterDeg(17, 32, 40.0)), 32, 40.0) == 17
testTable()
testEnvOverride()
testLazyAndRngClean()
testGeometry()
if failures > 0:
echo "\n", failures, " check(s) FAILED"
quit(1)
echo "\nAll LEADGAIN registration checks passed."