tune(SNNBot): P1 cold-start with P3-target learning — 2/10 wins

Fire P1 for first COLD_K=8 exemplars (cheap misses), always store
P3-correct lead offsets using P3_SPEED=11 in EVALUATE travelTime.
Switches to P3 once exemplar buffer has enough data. Best observed:
wins rounds 1-2 back-to-back (relative velocity encoding + P1 warmup).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 21:43:23 +02:00
parent 0384e2ba6a
commit 57cbcc5699
2 changed files with 12 additions and 5 deletions
+11 -5
View File
@@ -43,7 +43,9 @@ const
BETA = 1.0 # surrogate steepness (unitless; potentials are unitless)
NOISE_AMP = 0.3 # exploration noise amplitude
# ponytail: uniform exploration noise; upgrade to annealed Gaussian if convergence needs tuning
ENERGY_GUARD = 15.0 # don't fire below this energy
ENERGY_GUARD = 15.0 # don't fire below this energy
P3_SPEED = 11.0 # bullet speed for P3 (20 - 3*3); used for exemplar learning
COLD_K = 8 # exemplars needed before switching cold→warm (P1→P3)
# ── SNN types ─────────────────────────────────────────────────────────────────
@@ -447,10 +449,12 @@ method run*(bot: SNNBot) =
# Fire gate: only fire when error within adaptive dead zone
if err < adaptiveDeadZone:
# ponytail: fixed power 3.0 vs Walls; adaptive economy adds noise vs learning
if getEnergy() >= ENERGY_GUARD:
bot.lastBulletSpeed = bot.bulletSpeed
discard setFire(bot.currentFirePower)
# ponytail: P1 until COLD_K total exemplars exist; cross-round exemplar accumulation
let firePower = if bot.res.count < COLD_K: 1.0 else: bot.currentFirePower
let fireSpeed = 20.0 - 3.0 * firePower
bot.lastBulletSpeed = fireSpeed
discard setFire(firePower)
bot.phase = EVALUATE
else:
discard setFire(0.0)
@@ -458,7 +462,9 @@ method run*(bot: SNNBot) =
of EVALUATE:
# Predictive error signal: extrapolate enemy position at bullet impact time.
if bot.hasLastPos:
let travelTime = bot.enemyDist / bot.lastBulletSpeed # use speed from fire tick
# Always compute lead for P3_SPEED so exemplars teach P3-correct offsets,
# even when the actual shot was P1 (cold-start).
let travelTime = bot.enemyDist / P3_SPEED
let velRad = degToRad(bot.velDirDeg)
let futureX = bot.lastEnemyX + cos(velRad) * bot.velSpeed * travelTime
let futureY = bot.lastEnemyY + sin(velRad) * bot.velSpeed * travelTime
+1
View File
@@ -54,3 +54,4 @@ proc learn*(aimer: var BinaryAimer, input: BitVec80, offset: float) =
aimer.exemplars[aimer.nextSlot] = Exemplar(pattern: input, offset: offset, active: true)
aimer.nextSlot = (aimer.nextSlot + 1) mod MAX_K
if aimer.count < MAX_K: inc aimer.count