BitBrain gun (id 16): Pattern-relative ADE+SBC aim corrector, default off
Wire the verified common_libs/bitbrain ADE+SBC library into ModularBot as a
fine-grained angular corrector on top of Pattern's prediction, the shape the
offline gate test measured (argmax readout over N correction classes).
- common_libs/guns/bitbrain_gun.nim: new gun. Input = the existing TMHorizon
53 bits (tmhBaseBits + tmhLits); output = argmax class centre over
+-TR_BITBRAIN_RANGE, applied by rotating the Pattern point around the shooter
exactly as tmhApplyShift does. Label = the +h-tick fact from TmHorizonGun's
own observation ring (never across a round). Prequential (defer + resolve).
AD layer synthesised online for our binary inputs (center=0): heuristic
cold-start thresholds + running-histogram ~1% percentile init + the library's
adaptThresholds. Memory modes perRound (default, measured best) / retained /
decay (periodic partial SBC wipe). Lazy network build + local RNG, so the
default path builds nothing and consumes no global randomness.
- tm_horizon.nim: export tmhUpdateHistory and add tmhObservedAt (label seam).
- selector.nim: register BITBRAIN at rack id 16, default rmOff, in the SAME
commit as the id and the wiring (the aed579b admission bug is not repeated).
- ModularBot.nim: id 16 wired through predict/spawn/onResult/resets/colors,
arrays grown 16->17, spawn gated on rack admission, per-round/per-battle/
target reset hooks.
- env_report.nim: report every TR_BITBRAIN_* knob + add names to the known set.
- tests: update the rack length literals; new test_bitbrain_registration
(default-parity: off, lazy, global-RNG clean).
Guard counts unchanged: rack 48, tm_pattern_registration 20, vbullet_admit 12,
env_report 25, and the rest of the suite green.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
## Default-parity + registration guard for the BitBrain gun (id 16).
|
||||
##
|
||||
## No Java, no battle, no network build. Covers:
|
||||
## * the rack table carries BITBRAIN at id 16 and it defaults to `off`;
|
||||
## * the shipped rack still admits exactly Pattern;
|
||||
## * TR_RACK_BITBRAIN=both is what admits it, and the spawn gate honours it;
|
||||
## * `initBitBrainGun()` is LAZY (no network) 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_bitbrain_registration.nim
|
||||
|
||||
import std/[random, os, math]
|
||||
import gun_harness/virtual_bullets
|
||||
import gun_harness/selector
|
||||
import guns/bitbrain_gun
|
||||
|
||||
const BitBrainId = 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: BITBRAIN is registered at id 16", RackGunNames[BitBrainId] == "BITBRAIN"
|
||||
check "rack: BITBRAIN defaults to `off`", DefaultRackMembership[BitBrainId] == 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: BitBrain is NOT spawned under the default rack",
|
||||
not vBulletAdmitted(BitBrainId, rm1v1, DefaultRackMembership, true)
|
||||
|
||||
proc testEnvOverride() =
|
||||
clearRackEnv()
|
||||
putEnv("TR_RACK_BITBRAIN", "both")
|
||||
let m = loadRackMembership()
|
||||
check "env: TR_RACK_BITBRAIN=both admits BitBrain",
|
||||
m[BitBrainId] == rmBoth and
|
||||
vBulletAdmitted(BitBrainId, rm1v1, m, true)
|
||||
check "env: admitting BitBrain leaves Pattern as the only other member",
|
||||
admittedGuns(17, rm1v1, m) == @[PatternId, BitBrainId]
|
||||
clearRackEnv()
|
||||
|
||||
proc testLazyAndRngClean() =
|
||||
delEnv("TR_BITBRAIN_MEM")
|
||||
var g = initBitBrainGun()
|
||||
check "lazy: constructing the gun does NOT build the network (0 bytes)",
|
||||
g.networkBytes == 0
|
||||
check "default: unset TR_BITBRAIN_MEM is perRound",
|
||||
g.memMode == bmPerRound
|
||||
check "parse: retained/decay/unknown",
|
||||
parseMemMode("retained") == bmRetained and
|
||||
parseMemMode("decay") == bmDecay and
|
||||
parseMemMode("banana") == bmPerRound
|
||||
# Global RNG parity: constructing the gun must not consume global randomness.
|
||||
randomize(1234)
|
||||
let a = rand(1_000_000)
|
||||
randomize(1234)
|
||||
var g2 = initBitBrainGun()
|
||||
discard g2
|
||||
let b = rand(1_000_000)
|
||||
check "parity: initBitBrainGun() does not perturb the global RNG", a == b
|
||||
|
||||
proc testGeometry() =
|
||||
check "geometry: class 0 centre is the low edge + half a bin",
|
||||
abs(bbCenterDeg(0, 32, 40.0) - (-40.0 + 0.5 * 80.0 / 32.0)) < 1e-9
|
||||
check "geometry: bbClassOf round-trips the centre",
|
||||
bbClassOf(degToRad(bbCenterDeg(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 BitBrain registration checks passed."
|
||||
@@ -158,14 +158,14 @@ proc testDefaultsOnlyPattern() =
|
||||
DefaultRackMembership[12] == rmOff and DefaultRackMembership[13] == rmOff and
|
||||
DefaultRackMembership[14] == rmOff
|
||||
check "defaults: the default rack admits ONLY Pattern in 1v1",
|
||||
admittedGuns(15, rm1v1, DefaultRackMembership) == @[PatternId]
|
||||
admittedGuns(17, rm1v1, DefaultRackMembership) == @[PatternId]
|
||||
check "defaults: the default rack admits ONLY Pattern in melee",
|
||||
admittedGuns(15, rmMelee, DefaultRackMembership) == @[PatternId]
|
||||
admittedGuns(17, rmMelee, DefaultRackMembership) == @[PatternId]
|
||||
let loaded = loadRackMembership()
|
||||
check "defaults: with a clean environment loadRackMembership() == shipped table",
|
||||
loaded == DefaultRackMembership
|
||||
check "defaults: RackGunNames covers the shipped 16-gun rack",
|
||||
RackGunNames.len == 16 and DefaultRackMembership.len == 16
|
||||
check "defaults: RackGunNames covers the shipped 17-gun rack",
|
||||
RackGunNames.len == 17 and DefaultRackMembership.len == 17
|
||||
|
||||
proc testFloorRespectsAdmission() =
|
||||
## The FLOOR path (`bestRate <= 0` or below the floor rate) historically fell
|
||||
@@ -212,8 +212,8 @@ proc testRevertOverrideRestoresFullRack() =
|
||||
let want = if i == 14: rmOff else: rmBoth
|
||||
if m[i] != want: full = false
|
||||
check "revert: the documented one-liner restores the all-`both` full rack", full
|
||||
check "revert: 1v1 rack admits every gun again (all 14, TMPATTERN excluded)",
|
||||
admittedGuns(15, rm1v1, m) == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
|
||||
check "revert: 1v1 rack admits every gun again (TMPATTERN excluded)",
|
||||
admittedGuns(17, rm1v1, m) == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16]
|
||||
clearRackEnv()
|
||||
|
||||
proc testEnvOverrides() =
|
||||
|
||||
@@ -62,7 +62,7 @@ proc seedOldRack(t: var VirtualTracker, targetId: int) =
|
||||
# ── registration table ────────────────────────────────────────────────────────
|
||||
|
||||
proc testTable() =
|
||||
check "rack: RackGunNames has 16 entries", RackGunNames.len == 16
|
||||
check "rack: RackGunNames has 17 entries", RackGunNames.len == 17
|
||||
check "rack: the new gun is named TMPATTERN at id 14",
|
||||
RackGunNames[TmPatternId] == "TMPATTERN"
|
||||
check "rack: the new gun defaults to `off`",
|
||||
@@ -82,9 +82,9 @@ proc oldRackMembership(): array[15, RackMembership] =
|
||||
|
||||
proc testDefaultAdmitsOnlyPattern() =
|
||||
check "default membership admits only PATTERN (1v1)",
|
||||
admittedGuns(15, rm1v1, DefaultRackMembership) == @[PatternId]
|
||||
admittedGuns(17, rm1v1, DefaultRackMembership) == @[PatternId]
|
||||
check "default membership admits only PATTERN (melee)",
|
||||
admittedGuns(15, rmMelee, DefaultRackMembership) == @[PatternId]
|
||||
admittedGuns(17, rmMelee, DefaultRackMembership) == @[PatternId]
|
||||
|
||||
proc testEnvOverride() =
|
||||
for name in RackGunNames: delEnv("TR_RACK_" & name)
|
||||
|
||||
Reference in New Issue
Block a user