26b66cbb24
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.