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:
@@ -620,6 +620,15 @@ method onBulletHitWall*(bot: ModularBot, e: BulletHitWallEvent) =
|
||||
bot.strafeMover.removeBulletNear(e.bullet.x, e.bullet.y)
|
||||
bot.surfMover.removeBulletNear(e.bullet.x, e.bullet.y)
|
||||
bot.learnedMover.removeBulletNear(e.bullet.x, e.bullet.y)
|
||||
# Real wall endpoint of an ENEMY bullet. On the running server (0.35.5) a
|
||||
# BulletHitWallEvent is delivered only to the bullet's OWNER
|
||||
# (`addPrivateBotEvent(bullet.botId, ...)`), so this branch is a no-op live;
|
||||
# it is wired so a server that exposes enemy wall hit-points feeds the
|
||||
# exact-geometry wave resolution instead of the arrival-deadline proxy.
|
||||
if e.bullet.ownerId != getMyId():
|
||||
discard bot.learnedMover.resolveEnemyBullet(
|
||||
e.bullet.x, e.bullet.y, degToRad(e.bullet.direction),
|
||||
e.bullet.ownerId, bot.tick, false)
|
||||
|
||||
method onBulletHitBullet*(bot: ModularBot, e: BulletHitBulletEvent) =
|
||||
discard bot.resolveOwnBullet(e.bullet.bulletId) # bullet-vs-bullet: free the slot
|
||||
@@ -630,6 +639,13 @@ method onBulletHitBullet*(bot: ModularBot, e: BulletHitBulletEvent) =
|
||||
bot.strafeMover.removeBulletNear(e.bullet.x, e.bullet.y)
|
||||
bot.surfMover.removeBulletNear(e.bullet.x, e.bullet.y)
|
||||
bot.learnedMover.removeBulletNear(e.bullet.x, e.bullet.y)
|
||||
# `e.bullet` is OUR bullet; `e.hitBullet` is the bullet it struck. When that
|
||||
# is an ENEMY bullet, the event exposes its real endpoint + heading - a real
|
||||
# resolution for a wave that would otherwise wait for the deadline.
|
||||
if e.hitBullet.ownerId != getMyId() and e.hitBullet.bulletId != 0:
|
||||
discard bot.learnedMover.resolveEnemyBullet(
|
||||
e.hitBullet.x, e.hitBullet.y, degToRad(e.hitBullet.direction),
|
||||
e.hitBullet.ownerId, bot.tick, false)
|
||||
|
||||
method onHitByBullet*(bot: ModularBot, e: HitByBulletEvent) =
|
||||
# Feeds the ram bullet-rain abort window. Accumulate REAL ENERGY (the server's
|
||||
@@ -637,6 +653,11 @@ method onHitByBullet*(bot: ModularBot, e: HitByBulletEvent) =
|
||||
# energy/turn rate (see movements/ram_decision.bulletDamage).
|
||||
bot.ramDmgAccum += bulletDamage(e.bullet.power)
|
||||
bot.moveTracker.registerHit(e.bullet.power, e.bullet.direction, getX(), getY())
|
||||
# A real ENEMY bullet hit us: its endpoint (our impact point) + heading give
|
||||
# the wave's EXACT straight line, so resolve and drop that live wave now.
|
||||
discard bot.learnedMover.resolveEnemyBullet(
|
||||
e.bullet.x, e.bullet.y, degToRad(e.bullet.direction),
|
||||
e.bullet.ownerId, bot.tick, true)
|
||||
|
||||
proc enemyEnergyForReport(bot: ModularBot): float =
|
||||
## Last-scanned energy of the enemy the report is about (current target,
|
||||
|
||||
@@ -511,7 +511,7 @@ proc knownEnvNames*(): seq[string] =
|
||||
LearnedDecayEveryEnv, LearnedDecayShiftEnv, LearnedAlphaEnv,
|
||||
LearnedTravelEnv, LearnedReversalEnv, LearnedPrefDistEnv,
|
||||
LearnedDistBandEnv, LearnedRadialFracEnv, LearnedWallMarginEnv,
|
||||
LearnedGlobalEnv, LearnedLabelEnv, LearnedLogEnv,
|
||||
LearnedGlobalEnv, LearnedLabelEnv, LearnedRealEventsEnv, LearnedLogEnv,
|
||||
# harness vars (read by the test framework, inherited by the bot, so they
|
||||
# must NOT be reported as typos)
|
||||
"TR_SERVER_JAR", "TR_BATTLE_RUNNER", "TR_BATTLE_RUNNER_DIR",
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user