f58d65d2e8
Adds a per-sample intrinsic-confidence field (GunPrediction.confidence, threaded through FeedbackEvent/VirtualBullet, populated by Pattern, DecayGF, KNN, GuessFactor, Tsetlin, TMHorizon) and an offline recorder + analyzer that reproduce the paper's Figure 2 per gun and its Eq-8 composite. Measured on 3 held-out tr-bridge DrussGT battles (33k ticks, ~133k samples/gun): - FAITHFUL: DecayGF (rho +0.133), KNN (+0.090), Pattern (+0.064, weak). - GuessFactor is ANTI-faithful (rho -0.067); Tsetlin c_max is useless (0.001). - No pair of guns specialises complementarily: the same gun dominates both high-confidence slices in every pair. - Eq-8 alpha-normalised confidence-weighted composite: 18.41% vs Pattern 20.45% (McNemar p=3.1e-126). Faithful-only variant 18.68%, still loses. Shuffle control passes weakly (composite > shuffle, p=4e-14) so ~0.7pp of competence is real but ~2pp short. Offline veto: design is dead. See docs/tmcomposites_gate.md.
71 lines
2.6 KiB
Nim
71 lines
2.6 KiB
Nim
## Gun harness — shared types and Gun concept.
|
|
## Coordinate system: 0° = East, CCW positive (Tank Royale standard).
|
|
## Bullet speed formula: 20 - 3 * power.
|
|
|
|
import std/math
|
|
|
|
const BotRadius* = 18.0 ## hit detection radius in px
|
|
|
|
type
|
|
EnemyInfo* = object
|
|
id*: int
|
|
x*, y*: float
|
|
heading*, speed*, energy*: float
|
|
lastSeenTick*: int ## tracker tick this enemy was last scanned
|
|
## (the radar freshness signal; 0 if unknown)
|
|
|
|
WorldState* = object
|
|
## All raw data given to every gun every tick.
|
|
# Enemy (current TARGET)
|
|
enemyX*, enemyY*: float
|
|
enemySpeed*, enemyHeading*: float
|
|
# Self
|
|
selfX*, selfY*: float
|
|
selfSpeed*, selfHeading*: float
|
|
selfRadarHeading*: float
|
|
selfEnergy*: float
|
|
enemyEnergy*: float
|
|
# Arena
|
|
arenaWidth*, arenaHeight*: float
|
|
# Meta
|
|
tick*: int
|
|
# All alive enemies (for movement modules)
|
|
enemies*: seq[EnemyInfo]
|
|
|
|
GunPrediction* = object
|
|
## Absolute (x, y) where the gun predicts the enemy will be.
|
|
x*, y*: float
|
|
confidence*: float
|
|
## The gun's INTRINSIC, per-sample confidence in THIS prediction, on any
|
|
## positive scale the gun likes (the TMComposites gate normalises it by
|
|
## the sample range, Eq 7). 0.0 means "this gun has no confidence signal"
|
|
## (every deterministic geometric gun): such a gun casts no composite
|
|
## vote. Deliberately NOT a rolling hit-rate: that is the selector
|
|
## mechanism already measured negative (docs/gun_rack_analysis.md).
|
|
## See docs/tmcomposites_gate.md and common_libs/tests/measure_tmcomposites.nim.
|
|
|
|
FeedbackEvent* = object
|
|
## Outcome of a resolved virtual bullet.
|
|
prediction*: GunPrediction
|
|
actualX*, actualY*: float ## actual enemy position at resolution time
|
|
bulletPower*: float
|
|
fireTick*: int ## tick the virtual bullet was spawned (predict time)
|
|
powerBin*: int ## index into PowerBins the bullet belongs to
|
|
missDistance*: float ## px; < BotRadius = hit
|
|
hit*: bool
|
|
confidence*: float ## copied from the spawn-time GunPrediction (above)
|
|
|
|
proc bulletSpeed*(power: float): float {.inline.} =
|
|
20.0 - 3.0 * power
|
|
|
|
## Gun concept — any type T implementing these two procs is a valid gun.
|
|
template isGun*(T: typedesc): bool =
|
|
compiles(
|
|
block:
|
|
var g: T
|
|
let ws = WorldState()
|
|
let p: GunPrediction = g.predict(ws, 0.0)
|
|
let fe = FeedbackEvent()
|
|
g.onResult(fe)
|
|
)
|