tracker: corpses do not exist - revert the fix and retire the workaround
The belief "BotDeathEvent never reaches ModularBot, so enemyTracker keeps dead
enemies alive forever" was written into a code comment and then believed twice.
It is FALSE. Measured in a 7-bot melee with a per-tick probe comparing
enemyTracker's alive count against the server's getEnemyCount():
metric 1.3.1 (20 rd) 0.35.5 (15 rd)
observed enemy deaths 83 68
...non-round-ending 83 (100%) 66 (97%)
ekBotDeath events DROPPED 0 0
max dispatch lag (turns behind) 1 1
phantom ticks 1 / 16,820 1 / 12,596
MAX CORPSE LIFETIME 0 ticks 0 ticks
victims still alive at round end 0 0
onBotDeath fires for every death, including non-round-ending ones. The
API-level event-drop mechanism IS real (test_event_drop_mechanism.nim proves
it: ekBotDeath is not in isCritical and MAX_EVENTS_AGE=2) - the bot simply
never falls far enough behind for it to trigger (max lag 1 turn).
Removed:
- reconcileWithServer + ReconcilePersistTicks/mismatchTicks/sawServerAlive
(uncommitted, and ON BY DEFAULT despite the premise being false). Its own
comment admitted a shorter window once KILLED A LIVE ENEMY ("it fired three
more times after the tracker marked it dead") - a latent mis-prune path
defending against a bug that does not exist.
- The radar's CorpseTicks=40 filter and the same-class age>60 filter in
recordRadarStats, both carrying the false comment. Removal changes no real
behaviour: buildState feeds the radar enemyTracker.allAlive(), so a dead
enemy never reaches computeScan.
Kept:
- The TR_TRACKER_PROBE instrument (default OFF), which produced the table above.
- test_event_drop_mechanism.nim - the drop mechanism is a genuine library
behaviour worth guarding.
- isAlive/aliveCount on the tracker.
Added: docs/tracker_death_events.md (the durable negative, so this is not
re-invented a third time) and test_enemy_tracker_death.nim (13 checks) in place
of the test for the deleted feature.
Guards: test_gun_harness 39/39, test_vbullet_metric 11, test_power_selection 3,
test_adaptive_radar 41/41, test_event_drop_mechanism 6, test_enemy_tracker_death
13, acceptance 12/12, ModularBot compiles.
This commit is contained in:
@@ -318,9 +318,11 @@ proc testExpectedDisabled() =
|
||||
discard m.computeScan(ws(t, 0.0, @[enemyAt(1, 0.0, 300.0, float(t))]))
|
||||
check "gate: expectedEnemies = -1 does not block acquisition", m.phase == rpTrack
|
||||
|
||||
proc testCorpseIgnored() =
|
||||
# A dead-but-unmarked enemy (unseen > CorpseTicks) must not keep the radar in
|
||||
# acquisition, nor widen the arc.
|
||||
proc testStaleLiveEnemyTrusted() =
|
||||
# With the corpse filter retired the radar trusts the tracker: a tracked
|
||||
# enemy with a very old lastSeenTick is a stale LIVE enemy, not a corpse. It
|
||||
# is still included in the covering arc (so the sweep covers it) and, being
|
||||
# stale, keeps the radar in acquisition until it has been re-scanned.
|
||||
var m = initAdaptiveMeleeRadar()
|
||||
m.setExpectedEnemies(1)
|
||||
var t = 0
|
||||
@@ -328,27 +330,20 @@ proc testCorpseIgnored() =
|
||||
inc t
|
||||
discard m.computeScan(ws(t, 0.0, @[
|
||||
enemyAt(1, 100.0, 300.0, float(t)),
|
||||
enemyAt(2, 300.0, 300.0, float(t - 100)), # corpse, far bearing
|
||||
enemyAt(2, 300.0, 300.0, float(t - 100)), # stale, but alive
|
||||
]))
|
||||
check "corpse: a long-unseen enemy is ignored and tracking is reached",
|
||||
m.phase == rpTrack
|
||||
check "corpse: the corpse does not widen the swept arc",
|
||||
approx(m.lastSweptWidth, 2.0 * MarginDeg)
|
||||
|
||||
proc testCorpseExpectedGate() =
|
||||
# expectedEnemies = 2 but only one LIVE enemy (the other is a corpse): the
|
||||
# live count is 1, so the gate keeps the radar in acquisition.
|
||||
var m = initAdaptiveMeleeRadar()
|
||||
m.setExpectedEnemies(2)
|
||||
var t = 0
|
||||
for _ in 0 ..< FreshStreakTicks + 5:
|
||||
inc t
|
||||
discard m.computeScan(ws(t, 0.0, @[
|
||||
enemyAt(1, 100.0, 300.0, float(t)),
|
||||
enemyAt(2, 300.0, 300.0, float(t - 100)),
|
||||
]))
|
||||
check "corpse: the expected gate counts LIVE enemies only",
|
||||
# Bearings 100 and 300: the minimal covering arc runs 300 -> 100 (width 160).
|
||||
check "trust: a long-unseen tracked enemy is not dropped from the arc",
|
||||
approx(m.lastSweptWidth, 160.0 + 2.0 * MarginDeg)
|
||||
check "trust: a long-unseen tracked enemy keeps the radar in acquisition",
|
||||
m.phase == rpAcquire
|
||||
# When the tracker stops reporting it (onBotDeath marked it dead, so it is
|
||||
# absent from `state.enemies`) the radar trusts that and tracks the survivor.
|
||||
for _ in 0 ..< FreshStreakTicks + 2:
|
||||
inc t
|
||||
discard m.computeScan(ws(t, 0.0, @[enemyAt(1, 100.0, 300.0, float(t))]))
|
||||
check "trust: once the dead enemy leaves the tracker the radar tracks",
|
||||
m.phase == rpTrack
|
||||
|
||||
# ── driver ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -373,8 +368,7 @@ testWideArcFallback()
|
||||
testWideArcExitAndEnterHysteresis()
|
||||
testNoKnownEnemyStaysAcquire()
|
||||
testExpectedDisabled()
|
||||
testCorpseIgnored()
|
||||
testCorpseExpectedGate()
|
||||
testStaleLiveEnemyTrusted()
|
||||
|
||||
if failures > 0:
|
||||
echo "\n", failures, " check(s) FAILED"
|
||||
|
||||
Reference in New Issue
Block a user