Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.8 KiB
Existing Lead-Targeting & Enemy Tracking Code
Scope: all garages in SirRoboGarage as of research/lead-targeting branch.
Summary
No lead-targeting (bullet-travel-time prediction, fire-ahead calculation) exists anywhere in the repo. What does exist is well-factored enemy tracking, lateral-speed measurement, and a bullet-speed constant — all the primitives needed to implement lead targeting.
1. Enemy Tracker (PPO_Bot_garage)
File: PPO_Bot_garage/src/PPO_Bot/enemy_tracker.nim
The richest tracker in the repo. Provides:
| Feature | Detail |
|---|---|
EnemyState |
x, y, direction, speed, energy, ticksSinceLastScan, hasFired, lastFirePower |
| History ring | array[5, (x, y, direction, speed)] — sliding window of last 5 scan positions |
update() |
Called on ScannedBotEvent; detects enemy fire from energy delta in [0.1, 3.0] |
deadReckon() |
Projects position forward one tick using last known speed+direction |
getRadarTurnRate() |
Width-lock with overshoot arctan(36/dist) |
No lead angle calculation. History gives velocity vectors; deadReckon gives 1-tick projection;
but there is no travelTime = dist / bulletSpeed loop or atan2(lateral, forward) computation.
2. Bullet Speed Formula
Files: PPO_Bot_garage/src/PPO_Bot/state_vector.nim (line 109) and
SAC_LSTM_Bot_garage/src/SAC_LSTM_Bot/state.nim (line 105)
Both independently define:
let bSpeed = 20.0 - 3.0 * b.power # Tank Royale bullet speed formula
This is the authoritative formula. It appears in two garages but only for computing
ticksToImpact from the bullet's current position for the state vector (dodge context),
not for predicting where to aim before firing.
3. Lateral Speed Measurement (QBot_garage)
File: QBot_garage/src/QBot.nim (lines 100–105, 335–336)
# lateralSpeed: enemy velocity component perpendicular to our line of sight
let lateralSpeed = bot.enemySpeed * sin(degToRad(bot.enemyDir - bearingToEnemy))
QBot discretizes this into 4 bins (N_LAT_SPD = 4) as a state feature, then learns
Q-values for aim offsets [-12, -6, -3, 0, 3, 6, 12] degrees. This is the closest
thing to lead targeting in the repo: it captures why you'd lead, but the lead angle
itself is picked by the Q-table, not calculated analytically.
4. In-Flight Bullet Tracking (PPO_Bot_garage)
File: PPO_Bot_garage/src/PPO_Bot.nim (lines 70–74, 215–258)
PPO_Bot tracks enemy bullets in flight using a fixed array[4, InFlightBullet] (no heap).
Each bullet has (x, y, vx, vy, power) and is advanced each tick. Used to populate
the dodge-context slots in the state vector. No lead-targeting use.
5. State Vectors with Enemy History
PPO_Bot (PPO_Bot_garage/src/PPO_Bot/state_vector.nim)
- 57-float tensor
- Indices 22–41: 5-tick history window (x, y, direction, speed per tick)
- Indices 44–55: up to 3 enemy bullets (relX, relY, speed, ticksToImpact)
SAC_LSTM_Bot (SAC_LSTM_Bot_garage/src/SAC_LSTM_Bot/state.nim)
- 35-float tensor
- No explicit history window (LSTM handles temporal context)
- Indices 22–33: up to 3 enemy bullets
- Has
prevSpeed,prevDirectiononEnemyDatafor one-step acceleration/turn-rate
6. SNNBot (SNNBot_garage/src/SNNBot.nim)
Tracks only:
enemyBearing(absolute, from lastScannedBotEvent)enemyDisthasContact
No velocity, no history, no position tracking. The SNN input is population-coded relative bearing only. No lead targeting.
7. QBot (QBot_garage/src/QBot.nim)
Tracks enemyX, enemyY, enemyDir, enemySpeed, hasContact, ticksSinceScan.
Uses a 5-slot array[BULLET_BUF_CAP, BulletRecord] ring buffer for associating
Q-updates to bullet outcomes (hit/wall). This is a reward attribution ring buffer,
not a position history.
8. OscillatorBot (garage + adversary copy)
Files: OscillatorBot_garage/src/OscillatorBot.nim,
common_libs/test_framework/adversaries/OscillatorBot/src/OscillatorBot.nim
Head-on targeting only: aims gun directly at directionTo(myX, myY, e.x, e.y).
No lead. Both files are identical.
9. Targeting Utilities
File: PPO_Bot_garage/src/PPO_Bot/controllers.nim
proc aimToTick*(targetX, targetY, botX, botY, gunDirection: float): float
Rotates gun toward a target point (clamped to ±MAX_GUN_TURN_RATE). Used by PPO_Bot
via an (x,y) target the network chooses. No lead calculation inside — caller must
provide the predicted impact point if desired.
File: common_libs/radar_lock/radar_lock.nim
proc doRadar*(currentRadarHeading, enemyBearing: float64): float64
Width-lock with configurable overshoot (DefaultOvershootDeg = 5.0). Used by QBot,
SNNBot, and the PPO_Bot tracker's getRadarTurnRate.
10. Adversaries in common_libs/test_framework/adversaries/
Only two adversaries exist:
| Bot | Behavior |
|---|---|
SittingDuck |
Does nothing (stationary target) |
OscillatorBot |
Zigzag (±4°/tick turn, speed 7, reverses every 25 ticks), head-on gun |
No wall-bot, no spinner, no circler adversary.
Reuse Opportunities for SNNBot Lead Targeting
| Need | Reuse from |
|---|---|
| Enemy velocity (x, y, speed, dir) | EnemyTracker from PPO_Bot_garage/src/PPO_Bot/enemy_tracker.nim — drop-in, no bot API deps |
| Position history | Same: 5-slot array, pre-filled on cold-start |
| Bullet speed formula | bSpeed = 20.0 - 3.0 * power (inline constant, no import needed) |
| Radar lock | common_libs/radar_lock/radar_lock.nim — already used by SNNBot |
| Gun turn to point | aimToTick in PPO_Bot_garage/src/PPO_Bot/controllers.nim (or inline) |
The missing piece — the iterative lead-angle solver (for t in 0..20: if dist(predicted_pos(t), bullet_pos(t)) < eps: break) — does not exist anywhere in the repo and would need to be written.