## Shared helper: build the 13 ModularBot guns as type-erased offline range ## drivers, in ModularBot's gun-id order, with the same readiness gate the live ## loop uses (Tsetlin only spawns once its 10-frame window is full). import std/random import gun_harness/offline_range import guns/head_on import guns/linear import guns/circular import guns/tsetlin import guns/guess_factor import guns/pattern_matcher import guns/wall_bounce import guns/accel_predictor import guns/stop_shot import guns/displacement import guns/averaged_lead import guns/decay_gf import guns/knn_gun import guns/tm_selector proc buildAllGunDrivers*(seed = -1): seq[GunDriver] = ## seed >= 0 re-seeds the global RNG after constructing the stochastic guns ## (Tsetlin and the TM selector both call randomize() in their constructors), ## so their learning is reproducible for offline runs. ## ## Order matches ModularBot's gun ids exactly (TMSelect appended at 13). var tsetlin = initTsetlinGun() var tmSelector = initTmSelectorGun() if seed >= 0: randomize(seed) result = @[ makeDriver("HeadOn", HeadOnGun()), makeDriver("Linear", LinearGun()), makeDriver("Tsetlin", tsetlin), makeDriver("Circular", CircularGun()), makeDriver("GuessFactor", initGFGun()), makeDriver("Pattern", PatternMatcherGun()), makeDriver("WallBounce", initWallBounceGun()), makeDriver("Accel", initAccelGun()), makeDriver("StopShot", initStopShotGun()), makeDriver("Displace", initDisplacementGun()), makeDriver("AvgLead", initAveragedLeadGun()), makeDriver("DecayGF", initDecayGFGun()), makeDriver("KNN", initKNNGun()), makeDriver("TMSelect", tmSelector), ] proc makeTsetlinDriver*(seed = -1): tuple[driver: GunDriver, gun: ref TsetlinGun] = ## Same as makeDriver("Tsetlin", ...) but keeps a handle to the concrete gun, ## so a test can read its clause-sparsity / correction instrumentation after a ## replay. `makeDriver` heap-boxes a copy internally and drops the handle. let g = new(TsetlinGun) g[] = initTsetlinGun() if seed >= 0: randomize(seed) result.gun = g result.driver = GunDriver( name: "Tsetlin", predictCb: proc(state: WorldState, bulletSpeed: float): GunPrediction = g[].predict(state, bulletSpeed), resultCb: proc(e: FeedbackEvent) = g[].onResult(e), readyCb: proc(): bool = g[].isWarmedUp(), ) proc makeTmSelectorDriver*(seed = -1): tuple[driver: GunDriver, gun: ref TmSelectorGun] = ## Same as makeDriver("TMSelect", ...) but keeps a handle to the concrete gun ## so a test can inspect its votes / clause interpretability after a replay. let g = new(TmSelectorGun) g[] = initTmSelectorGun() if seed >= 0: randomize(seed) result.gun = g result.driver = GunDriver( name: "TMSelect", predictCb: proc(state: WorldState, bulletSpeed: float): GunPrediction = g[].predict(state, bulletSpeed), resultCb: proc(e: FeedbackEvent) = g[].onResult(e), readyCb: proc(): bool = g[].isWarmedUp(), )