Files
SirRoboGarage/PPO_Bot/PPO_Bot.nim
T
SirStone aa4bc77068 feat(PPO_Bot): network forward pass + action mapping (#14)
Two-hidden-layer MLP actor-critic (42→64→64→5/1) with stochastic
actorForward, logStd floor at -3, and BotAction mapper wired into
the run() loop. Assert-based test suite covers shapes, finiteness,
logStd collapse, and all action range bounds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-16 15:10:46 +02:00

58 lines
1.6 KiB
Nim

## PPO_Bot — enemy tracker + state vector wired into the game loop.
## Forward pass uses a random ActorCritic policy (weights not yet trained).
import std/os
import arraymancer
import tankroyale_botapi
import network
import actions
import ./enemy_tracker
import ./state_vector
const botJsonPath = currentSourcePath().parentDir / "PPO_Bot.json"
type PPOBot = ref object of Bot
tracker: EnemyTracker
var ac = initActorCritic()
method onScannedBot*(bot: PPOBot, e: ScannedBotEvent) =
bot.tracker.update(e.x, e.y, e.direction, e.speed, e.energy)
method onRoundStarted*(bot: PPOBot, e: RoundStartedEvent) =
bot.tracker = initEnemyTracker()
method run(bot: PPOBot) =
while isRunning():
bot.tracker.deadReckon()
setRadarTurnRate(bot.tracker.getRadarTurnRate(
getX(), getY(), getDirection(), getRadarDirection()))
let botData = BotStateData(
x: getX(),
y: getY(),
direction: getDirection(),
speed: getSpeed(),
energy: getEnergy(),
gunDirection: getGunDirection(),
gunHeat: getGunHeat(),
arenaWidth: float64(getArenaWidth()),
arenaHeight: float64(getArenaHeight()),
)
let state = buildStateVector(botData, bot.tracker)
let (rawActs, _) = ac.actorForward(state)
let acts = mapActions(rawActs, getSpeed().float32, getGunHeat().float32)
setTargetSpeed(acts.targetSpeed.float)
setTurnRate(acts.turnRate.float)
setGunTurnRate(acts.gunTurnRate.float)
if acts.shouldFire:
discard setFire(acts.firePower.float)
go()
when isMainModule:
var bot = PPOBot(tracker: initEnemyTracker())
start(bot, botJsonPath)