j131 learned movement: real bullet-endpoint resolution (TR_LEARNED_REAL_EVENTS, default off) + exact-geometry Gate A/B (inversion NOT fixed; state still the constraint)

This commit is contained in:
2026-09-26 11:54:11 +02:00
parent d5047598b5
commit cc332138b3
6 changed files with 569 additions and 14 deletions
@@ -88,6 +88,66 @@ proc counters(m: LearnedSurferModule): int =
if c != 0'u8: inc n
n
proc testRealEvents() =
## `TR_LEARNED_REAL_EVENTS`: a wave resolves on the REAL bullet event (exact
## origin->endpoint line) instead of the arrival deadline, and is dropped the
## moment it resolves (ghost cleanup). Default-off parity is also pinned.
const Ex = 400.0
const Ey = 100.0
const Ux = 400.0
const Uy = 300.0
proc wsAt(tick: int; eEnergy: float): WorldState =
WorldState(
enemyX: Ex, enemyY: Ey, enemyEnergy: eEnergy,
selfX: Ux, selfY: Uy, selfSpeed: 8.0, selfHeading: 0.0,
arenaWidth: 800.0, arenaHeight: 600.0, tick: tick,
enemies: @[EnemyInfo(id: 1, x: Ex, y: Ey,
heading: 180.0, speed: 0.0, energy: eEnergy)])
# ── parity: with the knob off a real event changes nothing ──────────────
putEnv(LearnedLabelEnv, "histogram")
putEnv(LearnedRealEventsEnv, "")
loadLearnedEnv()
var mOff = initLearnedSurfer()
discard mOff.computeMove(wsAt(0, 100.0)) # tick 0: baseline energy sample
discard mOff.computeMove(wsAt(1, 99.0)) # tick 1: a 1.0 firepower drop
check "RE default-off: the fire is detected as a live wave",
mOff.liveWaves == 1
check "RE default-off: resolveEnemyBullet is a no-op",
(not mOff.resolveEnemyBullet(Ux, Uy, degToRad(90.0), 1, 13, true)) and
mOff.liveWaves == 1
# ── ON: the exact origin->endpoint line resolves the wave immediately ────
putEnv(LearnedRealEventsEnv, "1")
loadLearnedEnv()
var mOn = initLearnedSurfer()
discard mOn.computeMove(wsAt(0, 100.0))
discard mOn.computeMove(wsAt(1, 99.0))
check "RE on: the fire is detected as a live wave", mOn.liveWaves == 1
# endpoint = our position on the centre line (GF 0 -> bin 15) at ~nominal
# (200 px at speed 17 -> ~12 ticks after the fire at tick 1)
check "RE on: the real hit endpoint resolves the wave",
mOn.resolveEnemyBullet(Ux, Uy, degToRad(90.0), 1, 13, true)
check "RE on: the wave is dropped at once (ghost cleanup)", mOn.liveWaves == 0
check "RE on: a real resolution is counted", mOn.resolvedReal == 1
check "RE on: the exact straight line lands in the centre bin",
mOn.glob[15] == 1
check "RE on: the real flight time is recorded", mOn.lastFlightErr > -20.0
# ── ON: an unmatched wave still resolves (as a WALL MISS) at the deadline ─
var mMiss = initLearnedSurfer()
discard mMiss.computeMove(wsAt(0, 100.0))
discard mMiss.computeMove(wsAt(1, 99.0))
check "RE on: a wave is pending before any event", mMiss.liveWaves == 1
for t in 2..<40: discard mMiss.computeMove(wsAt(t, 99.0))
check "RE on: the unmatched wall wave eventually resolves",
mMiss.liveWaves == 0 and mMiss.resolvedDead >= 1
putEnv(LearnedRealEventsEnv, "")
putEnv(LearnedLabelEnv, "")
loadLearnedEnv()
proc main() =
# 1. concept
check "MovementModule concept", isMovementModule(LearnedSurferModule)
@@ -170,6 +230,8 @@ proc main() =
putEnv(LearnedDecayEveryEnv, "")
loadLearnedEnv()
echo ""
testRealEvents()
echo ""
echo "checks=", checks, " failures=", failures
if failures > 0: quit(1)