23bdf8acd6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
170 lines
5.8 KiB
Markdown
170 lines
5.8 KiB
Markdown
# 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.
|