feat(gun_harness): per-gun REAL hit attribution + bestPower cold-start fix
Attribution is proven, not guessed: the server assigns a per-round-unique bulletId (GunEngine.nextBulletId) and stamps the same id on BulletFired, BulletHitBot, BulletHitWall and BulletHitBullet. Keep a FIFO of fired gun ids, stamp bulletId -> gunId on onBulletFired, resolve through that map. Hits are deferred when onBulletHit precedes onBulletFired in the same turn (client dispatches priority 70 > 60), which recovered 14 unattributed hits. 99.9% of shots and 99.8% of hits attributed. Stats lines now carry per-gun realShots/realHits/realHitRate; the old keys and round-level totals are unchanged. bestPower: a gun with zero observations in every bin previously returned the HIGHEST bin (power 3.0) because an empty bin satisfied the 'count == 0' clause on the first countdown iteration. Cold guns now return the lowest bin as the docstring always claimed. Warm-gun path untouched.
This commit is contained in:
@@ -161,6 +161,18 @@ proc bestPower*(t: VirtualTracker, gunId: GunId, targetId: int = -1): (int, floa
|
||||
## Uses per-enemy fitness when targetId >= 0 and data exists; else aggregate.
|
||||
let fit = t.fitnessFor(targetId)
|
||||
result = (0, PowerBins[0])
|
||||
# Cold gun (zero observations in every bin): fall back to the lowest power bin,
|
||||
# as documented. Without this the countdown loop below would hit the empty
|
||||
# highest bin first and wrongly return power 3.0.
|
||||
var anyObs = false
|
||||
for binIdx in 0..<len(PowerBins):
|
||||
if fit[gunId].bins[binIdx].count > 0:
|
||||
anyObs = true
|
||||
break
|
||||
if not anyObs:
|
||||
return (0, PowerBins[0])
|
||||
# Warm gun: unchanged — return the highest power bin clearing MinHitRate
|
||||
# (or an empty bin, which the existing logic treats as acceptable).
|
||||
for binIdx in countdown(len(PowerBins) - 1, 0):
|
||||
let rate = fit[gunId].bins[binIdx].hitRate()
|
||||
if rate >= MinHitRate or fit[gunId].bins[binIdx].count == 0:
|
||||
|
||||
Reference in New Issue
Block a user