fix(PPO_Bot): SIGSEGV crash fixes + static buffers for thread safety

- bullets: seq[InFlightBullet] → array[4, InFlightBullet] + bulletCount
  (eliminates cross-thread heap realloc under ORC)
- hasFired: edge-triggered (cleared after state build, not level-triggered)
- round_counter parseInt: wrapped for empty/torn file → 0
- Static SVG + intent buffers to kill cross-thread heap realloc
- Tick-local alive/bulletData also fixed arrays

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 14:23:08 +02:00
parent 75e32e3315
commit 6ad51148f4
11 changed files with 239 additions and 52 deletions
+30 -4
View File
@@ -1,4 +1,4 @@
## State vector builder — produces 44-float normalized tensor for PPO policy.
## State vector builder — produces 56-float normalized tensor for PPO policy.
## No bot API imports; takes plain BotState + EnemyTracker structs.
import std/math
@@ -16,12 +16,20 @@ type
gunHeat*: float64
arenaWidth*, arenaHeight*: float64
BulletData* = object
## Enemy bullet in flight (absolute arena coords + fire power).
x*, y*: float64
power*: float64 # fire power in [0.1, 3.0]; speed = 20 - 3*power
proc buildStateVector*(bot: BotStateData; enemy: EnemyTracker;
remainingGotoDistance: float64 = 0.0;
remainingGunAngle: float64 = 0.0): Tensor[float32] =
## Build the 44-float normalized state tensor.
remainingGunAngle: float64 = 0.0;
bullets: openArray[BulletData] = [];
bulletCount: int = 0): Tensor[float32] =
## Build the 56-float normalized state tensor.
## Indices 0-43: existing features. Indices 44-55: up to 3 bullet slots (4 floats each).
## All values clipped to roughly [-1, 1] via division by physical maxima.
result = zeros[float32](44)
result = zeros[float32](56)
let aW = bot.arenaWidth
let aH = bot.arenaHeight
@@ -91,3 +99,21 @@ proc buildStateVector*(bot: BotStateData; enemy: EnemyTracker;
# --- Goto controller inputs (indices 42-43) ---
result[42] = float32(remainingGotoDistance / diag)
result[43] = float32(remainingGunAngle / 180.0)
# --- Bullet tracking (indices 44-55): up to 3 enemy bullets, 4 floats each ---
# Per bullet: relX/aW, relY/aH, speed/20, ticksToImpact/diag
# Positions are relative to enemy (threat vector). Slots beyond bulletCount stay 0.
let ex = if enemy.hasContact: enemy.current.x else: bot.arenaWidth / 2.0
let ey = if enemy.hasContact: enemy.current.y else: bot.arenaHeight / 2.0
for i in 0 ..< min(bulletCount, 3):
let b = bullets[i]
let bSpeed = 20.0 - 3.0 * b.power # Tank Royale bullet speed formula
let bdx = b.x - ex
let bdy = b.y - ey
let bdist = sqrt(bdx * bdx + bdy * bdy)
let ticks = if bSpeed > 0.0: bdist / bSpeed else: 0.0
let base = 44 + i * 4
result[base + 0] = float32(bdx / bot.arenaWidth)
result[base + 1] = float32(bdy / bot.arenaHeight)
result[base + 2] = float32(bSpeed / 20.0)
result[base + 3] = float32(ticks / diag)