selector: arrival-accuracy tie-break measured NEGATIVE; randomness is load-bearing

Hypothesis under test (from the gun audit, which named the tie-band as "the
lever that matters most"): `bmPath` is deliberately generous (2.3-3.6x
`bmPoint`), so a gun can sit in the tied band on a ray that sweeps the target's
path while its bullets ARRIVE badly. So: keep the `path`-ranked band (path beat
point on real hit rate 7.43% vs 4.70%, z=5.56), but narrow the random draw
inside it using a parallel `point` (arrival-accuracy) window.

RESULT: NO EFFECT. Real DrussGT, ONE frozen binary (/tmp/ModularBot_tieband,
md5 2c0c56e6...), env knobs only, 7 runs x 7 rounds per arm, server-side events
sidecar, exact two-sided permutation test on per-run rates.

  arm                              runs  shots  real %  dmg/run   d      p
  tbbase (shipped)                    7   4128   7.17     175      --     --
  tbpt  path-rank + point-narrow      7   3938   7.08     165    +0.14  0.88
  tbpc  =commit control               7   3759   4.44      98    +2.74  0.0012
  tbpt25 point margin 0.25            7   3683   5.59     119    +1.65  0.20
  tbtie05 / tbtie40 (band width)      7   3937/3917  5.84/6.28  133/144  1.49/1.00  0.11/0.25
  tbwin50 (SelectorWindow=50)         7   3983   6.05     139    +1.20  0.11
  tbfloor10 (FloorPeakFrac=0.10)      7   3829   5.33     118    +2.12  0.11

tbpt vs base: fully overlapping ranges, p=0.88. This is a REAL null, not a dead
arm - the mechanism was live, and it visibly changed the selected-gun mix
(Pattern 24%->16%, Accel 6%->16%, Tsetlin ~0%->13%).

CONTROL VALIDATED, AND THIS IS THE THIRD TIME: removing the random draw inside
the band is SIGNIFICANTLY WORSE (4.44%, p=0.0012). Combined with the earlier
hysteresis A/B (7.02% -> 5.10% for commitment) and the light-hysteresis result,
the selector's per-tick randomness is now load-bearing on three independent
measurements. Narrowing the band on ANY second virtual statistic has not helped.

Every knob swept (band width, floor, window) is nominally worse than shipped at
n=7; that is "no credible win" rather than "proven harm" (sd ~1.8pp, ~1pp
resolution, underpowered).

Shipped default stays `GUN_SELECTOR_TIEBREAK=off`; the feature is opt-in, fully
guarded, and costs zero extra work on the default path (point windows are scored
only when the mode is on).

Guards: test_selector_tiebreak 19 (new, pure), test_gun_harness 39,
test_vbullet_metric 11, test_adaptive_radar 41, test_tfil_ring_weights 24,
test_power_policy 26, test_ram_decision 28, test_rack_membership 38,
acceptance_offline_vs_online 12/12 PASS (offline path calls neither
chooseFromFit nor the tie-break).

STRATEGIC CONCLUSION: three selection-side attempts have now failed (hysteresis,
commitment, point tie-break). The selector is at a local optimum and the
remaining lever is the QUALITY OF THE GUNS, not the selection among them.
This commit is contained in:
2026-09-22 01:07:12 +02:00
parent ca82053a11
commit 0ede6d12ec
4 changed files with 422 additions and 2 deletions
+178 -2
View File
@@ -128,6 +128,50 @@ proc parseSelectorMode*(value: string): SelectorMode =
let ActiveSelectorMode* = parseSelectorMode(getEnv(SelectorModeEnvVar, "relative"))
# ── arrival-accuracy tie-break (GUN_SELECTOR_TIEBREAK) ───────────────────────
#
# The shipped selector ranks guns by the `path` metric (the swept-ray score,
# measurably the better coarse signal: 7.43% vs 4.70% real hit rate) and then
# draws the shot UNIFORMLY at random inside a tie band built on that rate. The
# randomness is load-bearing (replacing it with commitment to the virtual best
# cost real hit rate, 7.02% -> 5.10%), but `path` is deliberately generous: it
# asks "does the ray ever sweep the target's path", which a gun can satisfy
# while its bullets ARRIVE poorly. Such a gun then sits inside the band and
# gets picked.
#
# The tie-break below keeps the band construction on `path` (and therefore the
# band's measured advantage) and makes the DRAW narrower by arrival accuracy:
# each virtual bullet is additionally scored with the `point` model (the
# prediction-accuracy score at the exact tick the bullet reaches its aim
# distance) into a parallel `pointBins` window, and the tied set is restricted
# to the guns whose point rate is within `GUN_SELECTOR_POINT_TIE` of the best
# point rate in the band. It is still a random draw inside a band — just a
# better-informed band — so the load-bearing randomness is preserved.
#
# `off` is the SHIPPED default, so an unset environment behaves exactly as
# before, and the parallel point scoring is not even performed.
const TieBreakEnvVar* = "GUN_SELECTOR_TIEBREAK"
type
TieBreakMode* = enum
tbOff ## DEFAULT: pure `path` band, uniform random draw.
tbPoint ## narrow the `path` tie band to the point-accurate guns.
tbPointCommit ## CONTROL (removes the randomness): take the best point rate
## inside the band, deterministically. Measured to be worse
## when done on the path rate; included so the point variant
## can be compared against its own commitment control.
proc parseTieBreak*(value: string): TieBreakMode =
## Empty / unknown values fall back to the shipped `off` and warn.
case value.strip().toLowerAscii()
of "", "off", "none", "0", "false": tbOff
of "point", "arrival", "p": tbPoint
of "commit", "pointcommit", "best": tbPointCommit
else:
stderr.writeLine("[gun_harness] unknown " & TieBreakEnvVar & "='" & value &
"'; falling back to 'off' (valid: off|point|commit)")
tbOff
# ── rack membership (melee vs 1v1) ────────────────────────────────────────────
#
# The selector can run two racks and switch between them on SERVER truth —
@@ -246,6 +290,25 @@ let ActiveShrink* = max(0.0, envFloat("GUN_SELECTOR_SHRINK", 20.0))
let ActiveDwellTicks* = max(0, envInt("GUN_SELECTOR_DWELL", GunDwellTicks))
let ActiveSwitchMargin* = max(0.0, envFloat("GUN_SELECTOR_MARGIN", GunSwitchMargin))
# Arrival-accuracy tie-break: mode plus the RELATIVE width of the point band
# (fraction of the best in-band point rate), matching the `RelTieMargin` style.
let ActiveTieBreak* = parseTieBreak(getEnv(TieBreakEnvVar, ""))
let ActivePointTie* = clamp(envFloat("GUN_SELECTOR_POINT_TIE", 0.5), 0.0, 1.0)
# A `point` tie-break needs the parallel point windows, which only the `path`
# metric records (under `point` the primary ranking already IS arrival
# accuracy, so the tie-break would be a no-op). Warn loudly rather than
# silently measuring nothing.
block:
if ActiveTieBreak != tbOff and ActiveMetric == bmPoint:
stderr.writeLine("[gun_harness] " & TieBreakEnvVar & "=" & $ActiveTieBreak &
" has no effect under " & MetricEnvVar & "=point (the primary " &
"ranking already uses arrival accuracy)")
elif ActiveTieBreak != tbOff:
# One audit line per process so a live run's log records which arm it ran.
stderr.writeLine("[gun_harness] " & TieBreakEnvVar & "=" & $ActiveTieBreak &
" pointTie=" & $ActivePointTie)
# Optional per-process seed so independent A/B runs use independent tie-breaks
# (Nim's default rand() stream is identical in every process, which would make
# "random" tie-breaks repeat across runs). Unset => leave the RNG untouched.
@@ -268,6 +331,8 @@ type
travelDist*: float ## accumulated px so far
fireDist*: float ## distance to target at fire time
active*: bool
pointScored*: bool ## the parallel point-model score for this flight has
## been recorded (tie-break mode only)
# --- path-metric bookkeeping (unused by the point metric) ---
hitSeen*: bool ## a swept segment already touched the target
bestMissDist*: float ## closest segment->target distance seen so far
@@ -282,6 +347,11 @@ type
GunFitness* = object
bins*: array[len(PowerBins), FitnessWindow]
pointBins*: array[len(PowerBins), FitnessWindow]
## Parallel `point`-model window, populated ONLY while the arrival-accuracy
## tie-break is active. The `path` ranking continues to read `bins`; the
## tie-break reads `pointBins` for the SAME bullets, so both scores come
## from one flight with no extra virtual bullets.
SelectorDiag* = object
## Optional observability for `chooseFromFit`/`bestGun`. Never needed by the
@@ -292,6 +362,9 @@ type
anyQualifies*: bool ## at least one gun reached MinObsBeforeCompete
floorRate*: float ## the floor actually applied this tick
incumbentKept*: bool ## hysteresis retained the incumbent this tick
pointTiedCount*: int ## guns kept after the arrival-accuracy tie-break
bestPointRate*: float ## best in-band point rate (the tie-break reference)
pointTieFired*: bool ## the tie-break actually narrowed the band
VirtualTracker* = object
bullets*: array[MaxBullets, VirtualBullet]
@@ -371,6 +444,7 @@ proc spawnBullets*(t: var VirtualTracker, gunId: GunId,
travelDist: 0.0,
fireDist: fireDist,
active: true,
pointScored: false,
hitSeen: false,
bestMissDist: Inf,
bestMissX: 0.0,
@@ -426,6 +500,31 @@ proc gunRate*(fit: GunFitness, pooled: bool): float =
let (h, n) = gunCounts(fit, pooled)
result = if n > 0: h.float / n.float else: 0.0
proc pointCounts*(fit: GunFitness, pooled: bool): tuple[hits, n: int] =
## Sample counts behind `pointRate`: the PARALLEL point-model window. All zero
## unless the arrival-accuracy tie-break is active, which is what makes the
## tie-break a graceful no-op on every existing caller.
if pooled:
for binIdx in 0..<len(PowerBins):
let m = min(fit.pointBins[binIdx].count, min(ActiveWindow, WindowSize))
result.n += m
result.hits += windowHits(fit.pointBins[binIdx], m)
else:
var best = -1.0
for binIdx in 0..<len(PowerBins):
let m = min(fit.pointBins[binIdx].count, min(ActiveWindow, WindowSize))
if m == 0: continue
let h = windowHits(fit.pointBins[binIdx], m)
let r = h.float / m.float
if r > best:
best = r
result = (h, m)
proc pointRate*(fit: GunFitness, pooled: bool): float =
## Arrival-accuracy rate over the parallel point window (0.0 when no data).
let (h, n) = pointCounts(fit, pooled)
result = if n > 0: h.float / n.float else: 0.0
proc rankScore(fit: GunFitness, pooled: bool, stat: RankStat,
fieldRate, shrinkK: float): float =
## Ranking statistic over the gun's window. All are monotone-ish in the mean,
@@ -478,7 +577,8 @@ proc noteBestRate*(t: var VirtualTracker) =
proc tickBullets*(t: var VirtualTracker, state: WorldState,
enemies: Table[int, tuple[x, y: float, lastSeenTick: int, alive: bool]],
onResolved: proc(gunId: GunId, binIdx: int, e: FeedbackEvent)) =
onResolved: proc(gunId: GunId, binIdx: int, e: FeedbackEvent),
tieBreak: TieBreakMode = ActiveTieBreak) =
## Advance all active bullets one tick.
##
## The `bmPoint` branch (default) is unchanged: resolve when the bullet
@@ -563,6 +663,20 @@ proc tickBullets*(t: var VirtualTracker, state: WorldState,
let by = b.fireY + uy * b.travelDist
let segMiss = distPointToSegment(ex, ey, ax, ay, bx, by)
# Arrival-accuracy probe (tie-break only; skipped entirely when off). The
# tick the bullet first reaches its fire-time aim distance is exactly the
# tick `bmPoint` would resolve on, so this records the SAME outcome the
# point model would have — just into a parallel window, leaving the path
# score (and therefore the shipped ranking) untouched.
if tieBreak != tbOff and not b.pointScored and b.travelDist >= b.fireDist:
b.pointScored = true
let (px, py) =
if dist < 1e-6: (b.aimX, b.aimY)
else: (b.fireX + ux * b.fireDist, b.fireY + uy * b.fireDist)
let pMiss = hypot(px - ex, py - ey)
if b.targetId in t.fitness:
t.fitness[b.targetId][b.gunId].pointBins[b.powerBin].record(pMiss < BotRadius)
if not b.hitSeen:
if segMiss < BotRadius:
# First physical contact — freeze it so a later closer approach
@@ -627,6 +741,11 @@ proc fitnessFor*(t: VirtualTracker, targetId: int): seq[GunFitness] =
let src = perEnemy[gunId].bins[binIdx]
for k in 0..<min(src.count, WindowSize):
result[gunId].bins[binIdx].record(src.hits[k])
# The parallel point window (tie-break) must be merged by the same rule,
# or the aggregate fallback would silently lose the arrival score.
let psrc = perEnemy[gunId].pointBins[binIdx]
for k in 0..<min(psrc.count, WindowSize):
result[gunId].pointBins[binIdx].record(psrc.hits[k])
proc bestPower*(t: VirtualTracker, gunId: GunId, targetId: int = -1): (int, float) =
## Returns (binIdx, power). Prefers the HIGHEST power bin whose virtual hit
@@ -760,7 +879,9 @@ proc chooseFromFit*(fit: seq[GunFitness], diag: ptr SelectorDiag = nil,
incumbent: GunId = -1,
switchMargin = 0.0,
rackMode: RackMode = rm1v1,
membership: openArray[RackMembership] = []): GunId =
membership: openArray[RackMembership] = [],
tieBreak: TieBreakMode = ActiveTieBreak,
pointTieMargin: float = ActivePointTie): GunId =
## Core gun ranking over an already-resolved fitness seq. Split out from
## `bestGun` so the offline range can rank without copying a VirtualTracker,
## and so callers can request `diag` for the selection internals.
@@ -789,6 +910,12 @@ proc chooseFromFit*(fit: seq[GunFitness], diag: ptr SelectorDiag = nil,
## runs ONLY when a switch is actually permitted — a tick that retains the
## incumbent returns before `rand`, so the tie-break no longer re-decides
## every tick.
##
## `tieBreak` (see `TieBreakMode`) optionally narrows the tied set by the
## PARALLEL arrival-accuracy window (`GunFitness.pointBins`, filled by
## `tickBullets` only while the tie-break is active) before that random draw.
## The default is the shipped `off`, and a fitness seq with no point data
## leaves the band untouched, so every existing caller is byte-identical.
let pooled = if mode == smRelative: ActivePooled else: false
let admitted = admittedGuns(fit.len, rackMode, membership)
@@ -877,6 +1004,55 @@ proc chooseFromFit*(fit: seq[GunFitness], diag: ptr SelectorDiag = nil,
return incumbent
if tied.len == 0: return 0
# ── arrival-accuracy tie-break (GUN_SELECTOR_TIEBREAK) ──────────────────────
# `tied` is the `path` band (optionally already narrowed to the challengers
# that cleared the switch margin). Re-order it by the parallel point window so
# the uniform draw below lands on guns whose bullets actually ARRIVE, not just
# on guns whose ray sweeps the target generously.
#
# tbPoint — narrow the band to guns within `pointTieMargin` of the best
# in-band point rate, then keep the uniform random draw.
# tbPointCommit — CONTROL: deterministically take the best point rate,
# removing the random draw. Included so the narrowed band can
# be compared against its own commitment control.
#
# A gun with NO point samples is KEPT: it cannot be judged, and dropping it
# would turn "cold" into "bad". If no tied gun has any point data the band is
# left untouched, so the tie-break is a strict no-op until the point window
# warms up.
if tieBreak != tbOff and tied.len > 1:
var bestPoint = 0.0
var anyPoint = false
for g in tied:
let (h, n) = pointCounts(fit[g], pooled = true)
if n == 0: continue
anyPoint = true
bestPoint = max(bestPoint, h.float / n.float)
if diag != nil: diag[].bestPointRate = bestPoint
if anyPoint and bestPoint > 0.0:
if tieBreak == tbPointCommit:
var bestGun = tied[0]
var bestP = -1.0
for g in tied:
let p = pointRate(fit[g], pooled = true)
if p > bestP:
bestP = p
bestGun = g
if diag != nil:
diag[].pointTieFired = true
diag[].pointTiedCount = 1
return bestGun
var kept: seq[GunId]
for g in tied:
let (h, n) = pointCounts(fit[g], pooled = true)
if n == 0 or h.float / n.float >= bestPoint * (1.0 - pointTieMargin):
kept.add g
if kept.len > 0 and kept.len < tied.len:
if diag != nil: diag[].pointTieFired = true
tied = kept
if diag != nil: diag[].pointTiedCount = kept.len
result = tied[rand(tied.len - 1)]
proc bestGun*(t: VirtualTracker, targetId: int = -1,
@@ -0,0 +1,185 @@
## Pure guard for the selector's ARRIVAL-ACCURACY TIE-BREAK
## (`GUN_SELECTOR_TIEBREAK`, see common_libs/gun_harness/virtual_bullets.nim).
##
## No Java, no server, no battle:
## nim c -r common_libs/tests/test_selector_tiebreak.nim
##
## Two things are pinned here:
## 1. the RANKING rule — `chooseFromFit` narrows a `path` tie band by the
## parallel `point` (arrival-accuracy) window while still drawing the shot
## at random inside the narrowed band;
## 2. the RECORDING rule — `tickBullets` fills that parallel window from the
## exact tick the bullet reaches its aim distance, leaving the `path` score
## untouched.
import std/[math, random, tables, strformat]
import gun_harness/gun_interface
import gun_harness/virtual_bullets
var failures = 0
proc check(name: string, ok: bool) =
if ok: echo "PASS: ", name
else: echo "FAIL: ", name; inc failures
proc recordHit(fw: var FitnessWindow, hit: bool) =
fw.hits[fw.head] = hit
fw.head = (fw.head + 1) mod WindowSize
inc fw.count
proc seed(fit: var GunFitness, binIdx, hits, misses: int, point: bool) =
## Record `hits`/`misses` into the gun's path window (or the parallel point
## window when `point`).
var fw = if point: addr fit.pointBins[binIdx] else: addr fit.bins[binIdx]
for _ in 0..<hits: recordHit(fw[], true)
for _ in 0..<misses: recordHit(fw[], false)
proc mkFit(rates: openArray[tuple[pHits, pMiss, aHits, aMiss: int]]): seq[GunFitness] =
## One gun per entry: (path hits, path misses, point hits, point misses) in
## power bin 0. Both windows get the same >= MinObsBeforeCompete sample count.
result = newSeq[GunFitness](rates.len)
for i, r in rates:
result[i].seed(0, r.pHits, r.pMiss, point = false)
result[i].seed(0, r.aHits, r.aMiss, point = true)
# ── parsing / default ─────────────────────────────────────────────────────────
proc testParsing() =
check "tiebreak parse: empty -> shipped default (off)", parseTieBreak("") == tbOff
check "tiebreak parse: 'off' -> tbOff", parseTieBreak("off") == tbOff
check "tiebreak parse: 'point' -> tbPoint", parseTieBreak("point") == tbPoint
check "tiebreak parse: 'commit' -> tbPointCommit", parseTieBreak("commit") == tbPointCommit
check "tiebreak parse: case/space insensitive", parseTieBreak(" PoInT ") == tbPoint
check "tiebreak parse: unknown -> off (safe fallback, warns)",
parseTieBreak("definitely-not-a-mode") == tbOff
# ── ranking rule ──────────────────────────────────────────────────────────────
proc testNarrowingKeepsRandomness() =
## Three path-tied guns (10.0 / 9.5 / 9.0 %), all inside the 20% relative
## band. Arrival accuracy is sharply different: 1 / 4 / 3 %. With a 0.5
## relative point margin the band must lose gun 0 (0.01 < 0.5 * 0.04) but keep
## the uniform random draw over guns 1 and 2.
randomize(20240921)
let fit = mkFit([(100, 0, 1, 99), (95, 5, 40, 60), (90, 10, 30, 70)])
var seen: array[3, int]
var narrowed = 0
for _ in 0..<400:
var d: SelectorDiag
let g = chooseFromFit(fit, addr d, mode = smRelative,
tieBreak = tbPoint, pointTieMargin = 0.5)
if g >= 0 and g < 3: inc seen[g]
if d.pointTieFired and d.pointTiedCount == 2: inc narrowed
check "tiebreak(point): the bad-arrival gun is never drawn", seen[0] == 0
let bothDrawn = seen[1] > 0 and seen[2] > 0
check "tiebreak(point): both point-accurate tied guns are still drawn (randomness kept)", bothDrawn
check "tiebreak(point): the band really was narrowed (diag)", narrowed == 400
echo fmt" point draws: gun0={seen[0]} gun1={seen[1]} gun2={seen[2]} (n=400)"
proc testOffIsBaseline() =
## With the tie-break OFF the identical fitness set must still expose the
## bad-arrival gun — i.e. the shipped behaviour is unchanged.
randomize(20240921)
let fit = mkFit([(100, 0, 1, 99), (95, 5, 40, 60), (90, 10, 30, 70)])
var seen: array[3, int]
for _ in 0..<400:
let g = chooseFromFit(fit, nil, mode = smRelative, tieBreak = tbOff)
if g >= 0 and g < 3: inc seen[g]
check "tiebreak(off): the bad-arrival gun is still drawn (no behaviour change)",
seen[0] > 0 and seen[1] > 0 and seen[2] > 0
echo fmt" off draws: gun0={seen[0]} gun1={seen[1]} gun2={seen[2]} (n=400)"
proc testCommitIsDeterministic() =
## Control arm: `tbPointCommit` removes the random draw and takes the best
## in-band arrival rate.
randomize(1)
let fit = mkFit([(100, 0, 1, 99), (95, 5, 40, 60), (90, 10, 30, 70)])
var allOne = true
for _ in 0..<200:
if chooseFromFit(fit, nil, mode = smRelative,
tieBreak = tbPointCommit) != 1: allOne = false
check "tiebreak(commit): deterministically returns the best arrival gun", allOne
proc testColdPointIsNoOp() =
## With no point samples at all the tie-break must leave the band untouched.
randomize(7)
let fit = mkFit([(100, 0, 0, 0), (95, 5, 0, 0), (90, 10, 0, 0)])
var seen: array[3, int]
var fired = 0
for _ in 0..<400:
var d: SelectorDiag
let g = chooseFromFit(fit, addr d, mode = smRelative, tieBreak = tbPoint)
if g >= 0 and g < 3: inc seen[g]
if d.pointTieFired: inc fired
check "tiebreak(cold point data): band untouched, all tied guns drawn",
seen[0] > 0 and seen[1] > 0 and seen[2] > 0
check "tiebreak(cold point data): never reports a narrowing", fired == 0
proc testColdGunIsKept() =
## A gun with ZERO point samples cannot be judged and must be KEPT, while a
## gun with a measured bad arrival rate is dropped.
randomize(11)
let fit = mkFit([(100, 0, 0, 0), (95, 5, 40, 60), (90, 10, 1, 99)])
var seen: array[3, int]
for _ in 0..<400:
let g = chooseFromFit(fit, nil, mode = smRelative,
tieBreak = tbPoint, pointTieMargin = 0.5)
if g >= 0 and g < 3: inc seen[g]
check "tiebreak: a cold-on-point gun is kept (not judged bad)", seen[0] > 0
check "tiebreak: a measured bad-arrival gun is dropped", seen[2] == 0
check "tiebreak: the point-accurate gun is still drawn", seen[1] > 0
echo fmt" cold/mixed draws: gun0={seen[0]} gun1={seen[1]} gun2={seen[2]}"
# ── recording rule (tickBullets) ──────────────────────────────────────────────
proc mkState(tick: int, ex, ey: float): WorldState =
WorldState(selfX: 100.0, selfY: 100.0,
enemyX: ex, enemyY: ey, enemySpeed: 0.0, enemyHeading: 0.0,
arenaWidth: 1000.0, arenaHeight: 1000.0, tick: tick)
proc runOneBullet(tieBreak: TieBreakMode): tuple[samples, hits: int] =
## Spawn one bullet per power bin aimed at a stationary enemy 200 px away, then
## tick until it passes its aim distance. The parallel window must fill only
## when the tie-break is active.
let targetId = 7
var t = initTracker(1, bmPath)
let s0 = mkState(0, 300.0, 100.0)
let preds = [GunPrediction(x: 300.0, y: 100.0), GunPrediction(x: 300.0, y: 100.0),
GunPrediction(x: 300.0, y: 100.0), GunPrediction(x: 300.0, y: 100.0)]
t.spawnBullets(0, preds, s0, targetId)
for i in 1..<80:
let st = mkState(i, 300.0, 100.0)
var enemies: Table[int, tuple[x, y: float, lastSeenTick: int, alive: bool]]
enemies[targetId] = (x: st.enemyX, y: st.enemyY, lastSeenTick: st.tick, alive: true)
t.tickBullets(st, enemies,
proc(gid: GunId, bin: int, e: FeedbackEvent) = discard, tieBreak = tieBreak)
let fit = t.fitnessFor(targetId)
for bin in 0..<len(PowerBins):
let n = min(fit[0].pointBins[bin].count, WindowSize)
result.samples += n
for k in 0..<n:
if fit[0].pointBins[bin].hits[k]: inc result.hits
proc testRecording() =
let on = runOneBullet(tbPoint)
let off = runOneBullet(tbOff)
check "recording: tie-break OFF records no parallel samples (zero overhead)",
off.samples == 0
check "recording: tie-break ON records one parallel sample per bullet",
on.samples == len(PowerBins)
check "recording: an aimed stationary target is a point hit",
on.hits == on.samples
# ── driver ────────────────────────────────────────────────────────────────────
testParsing()
testNarrowingKeepsRandomness()
testOffIsBaseline()
testCommitIsDeterministic()
testColdPointIsNoOp()
testColdGunIsKept()
testRecording()
if failures > 0:
echo "\n", failures, " check(s) FAILED"
quit(1)
echo "\nAll selector tie-break checks passed."