research(SNNBot): survey existing lead-targeting and enemy tracking code

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-13 20:30:10 +02:00
parent dbae449273
commit 23bdf8acd6
@@ -0,0 +1,169 @@
# 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:
```nim
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)
```nim
# 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`, `prevDirection` on `EnemyData` for one-step acceleration/turn-rate
---
## 6. SNNBot (`SNNBot_garage/src/SNNBot.nim`)
Tracks only:
- `enemyBearing` (absolute, from last `ScannedBotEvent`)
- `enemyDist`
- `hasContact`
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`
```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`
```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.