|
|
|
@@ -0,0 +1,145 @@
|
|
|
|
|
## RulesProbe — measures Tank Royale server physics empirically.
|
|
|
|
|
##
|
|
|
|
|
## Stationary bot that aims at a stationary adversary and cycles through a list
|
|
|
|
|
## of firepowers, logging everything needed to read bullet damage, bullet speed,
|
|
|
|
|
## gun heat / cooling rate and the round-result score breakdown straight off the
|
|
|
|
|
## server's events. Used to compare server 0.35.5 against 1.3.1.
|
|
|
|
|
##
|
|
|
|
|
## Env:
|
|
|
|
|
## PROBE_LOG output JSONL path (default /tmp/probe_log.jsonl)
|
|
|
|
|
## PROBE_POWERS comma-separated firepowers to cycle (default 0.1,0.5,1,2,3)
|
|
|
|
|
|
|
|
|
|
import std/[os, strutils, json, math, tables]
|
|
|
|
|
import robocode_tankroyale_botapi
|
|
|
|
|
|
|
|
|
|
const botJsonPath = currentSourcePath().parentDir / "RulesProbe.json"
|
|
|
|
|
|
|
|
|
|
let logPath = getEnv("PROBE_LOG", "/tmp/probe_log.jsonl")
|
|
|
|
|
let powerList =
|
|
|
|
|
block:
|
|
|
|
|
var s: seq[float]
|
|
|
|
|
for tok in getEnv("PROBE_POWERS", "0.1,0.5,1,2,3").split(','):
|
|
|
|
|
let t = tok.strip()
|
|
|
|
|
if t.len > 0:
|
|
|
|
|
try: s.add parseFloat(t)
|
|
|
|
|
except ValueError: discard
|
|
|
|
|
if s.len == 0: s = @[1.0]
|
|
|
|
|
s
|
|
|
|
|
|
|
|
|
|
var gLog: File
|
|
|
|
|
var gReady = false
|
|
|
|
|
|
|
|
|
|
proc logRow(row: JsonNode) =
|
|
|
|
|
if not gReady:
|
|
|
|
|
try:
|
|
|
|
|
gLog = open(logPath, fmAppend)
|
|
|
|
|
gReady = true
|
|
|
|
|
except CatchableError:
|
|
|
|
|
return
|
|
|
|
|
try:
|
|
|
|
|
gLog.writeLine($row)
|
|
|
|
|
gLog.flushFile()
|
|
|
|
|
except CatchableError:
|
|
|
|
|
discard
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
|
RulesProbe = ref object of Bot
|
|
|
|
|
powerIdx: int
|
|
|
|
|
targetX, targetY: float
|
|
|
|
|
hasTarget: bool
|
|
|
|
|
fireTick: Table[int, int]
|
|
|
|
|
fireX, fireY: Table[int, float]
|
|
|
|
|
fireDir: Table[int, float]
|
|
|
|
|
firePower: Table[int, float]
|
|
|
|
|
scanX, scanY: Table[int, float]
|
|
|
|
|
|
|
|
|
|
method onScannedBot*(bot: RulesProbe, e: ScannedBotEvent) =
|
|
|
|
|
bot.targetX = e.x
|
|
|
|
|
bot.targetY = e.y
|
|
|
|
|
bot.hasTarget = true
|
|
|
|
|
bot.scanX[e.scannedBotId] = e.x
|
|
|
|
|
bot.scanY[e.scannedBotId] = e.y
|
|
|
|
|
logRow(%*{"ev": "scan", "t": e.turnNumber, "id": e.scannedBotId,
|
|
|
|
|
"x": e.x, "y": e.y, "e": e.energy})
|
|
|
|
|
|
|
|
|
|
method onBulletFired*(bot: RulesProbe, e: BulletFiredEvent) =
|
|
|
|
|
let b = e.bullet
|
|
|
|
|
bot.fireTick[b.bulletId] = e.turnNumber
|
|
|
|
|
bot.fireX[b.bulletId] = b.x
|
|
|
|
|
bot.fireY[b.bulletId] = b.y
|
|
|
|
|
bot.fireDir[b.bulletId] = b.direction
|
|
|
|
|
bot.firePower[b.bulletId] = b.power
|
|
|
|
|
logRow(%*{"ev": "fire", "t": e.turnNumber, "bid": b.bulletId, "p": b.power,
|
|
|
|
|
"x": b.x, "y": b.y, "dir": b.direction})
|
|
|
|
|
|
|
|
|
|
method onBulletHit*(bot: RulesProbe, e: BulletHitBotEvent) =
|
|
|
|
|
let b = e.bullet
|
|
|
|
|
let dist = if b.bulletId in bot.fireX:
|
|
|
|
|
hypot(bot.scanX.getOrDefault(e.victimId, 0.0) - bot.fireX[b.bulletId],
|
|
|
|
|
bot.scanY.getOrDefault(e.victimId, 0.0) - bot.fireY[b.bulletId])
|
|
|
|
|
else: -1.0
|
|
|
|
|
logRow(%*{"ev": "hitbot", "t": e.turnNumber, "bid": b.bulletId, "p": b.power,
|
|
|
|
|
"dmg": e.damage, "e": e.energy, "victim": e.victimId,
|
|
|
|
|
"ft": bot.fireTick.getOrDefault(b.bulletId, -1), "dist": dist})
|
|
|
|
|
|
|
|
|
|
method onBulletHitWall*(bot: RulesProbe, e: BulletHitWallEvent) =
|
|
|
|
|
let b = e.bullet
|
|
|
|
|
logRow(%*{"ev": "hitwall", "t": e.turnNumber, "bid": b.bulletId, "p": b.power,
|
|
|
|
|
"x": b.x, "y": b.y, "dir": b.direction,
|
|
|
|
|
"ft": bot.fireTick.getOrDefault(b.bulletId, -1),
|
|
|
|
|
"fx": bot.fireX.getOrDefault(b.bulletId, 0.0),
|
|
|
|
|
"fy": bot.fireY.getOrDefault(b.bulletId, 0.0),
|
|
|
|
|
"fdir": bot.fireDir.getOrDefault(b.bulletId, 0.0)})
|
|
|
|
|
|
|
|
|
|
method onHitByBullet*(bot: RulesProbe, e: HitByBulletEvent) =
|
|
|
|
|
logRow(%*{"ev": "hitby", "t": e.turnNumber, "p": e.bullet.power,
|
|
|
|
|
"dmg": e.damage, "e": e.energy})
|
|
|
|
|
|
|
|
|
|
method onRoundEnded*(bot: RulesProbe, e: RoundEndedEventForBot) =
|
|
|
|
|
let r = e.results
|
|
|
|
|
logRow(%*{"ev": "roundend", "t": e.turnNumber, "round": e.roundNumber,
|
|
|
|
|
"rank": r.rank, "survival": r.survival,
|
|
|
|
|
"lastSurvivorBonus": r.lastSurvivorBonus,
|
|
|
|
|
"bulletDamage": r.bulletDamage, "bulletKillBonus": r.bulletKillBonus,
|
|
|
|
|
"ramDamage": r.ramDamage, "ramKillBonus": r.ramKillBonus,
|
|
|
|
|
"totalScore": r.totalScore, "firstPlaces": r.firstPlaces,
|
|
|
|
|
"secondPlaces": r.secondPlaces, "thirdPlaces": r.thirdPlaces})
|
|
|
|
|
|
|
|
|
|
method onGameEnded*(bot: RulesProbe, e: GameEndedEventForBot) =
|
|
|
|
|
let r = e.results
|
|
|
|
|
logRow(%*{"ev": "gameend", "rounds": e.numberOfRounds,
|
|
|
|
|
"rank": r.rank, "survival": r.survival,
|
|
|
|
|
"lastSurvivorBonus": r.lastSurvivorBonus,
|
|
|
|
|
"bulletDamage": r.bulletDamage, "bulletKillBonus": r.bulletKillBonus,
|
|
|
|
|
"ramDamage": r.ramDamage, "ramKillBonus": r.ramKillBonus,
|
|
|
|
|
"totalScore": r.totalScore})
|
|
|
|
|
|
|
|
|
|
method run*(bot: RulesProbe) =
|
|
|
|
|
setAdjustGunForBodyTurn(true)
|
|
|
|
|
setAdjustRadarForBodyTurn(true)
|
|
|
|
|
setAdjustRadarForGunTurn(true)
|
|
|
|
|
setTargetSpeed(0.0)
|
|
|
|
|
setTurnRate(0.0)
|
|
|
|
|
logRow(%*{"ev": "start", "turn": getTurn(), "arenaW": getArenaWidth(),
|
|
|
|
|
"arenaH": getArenaHeight(), "cooling": getGunCoolingRate(),
|
|
|
|
|
"powers": powerList})
|
|
|
|
|
while isRunning():
|
|
|
|
|
let t = getTurn()
|
|
|
|
|
logRow(%*{"ev": "tick", "t": t, "heat": getGunHeat(), "e": getEnergy(),
|
|
|
|
|
"x": getX(), "y": getY(), "gd": getGunDirection(),
|
|
|
|
|
"rd": getRadarDirection()})
|
|
|
|
|
setRadarTurnRate(45.0)
|
|
|
|
|
if bot.hasTarget:
|
|
|
|
|
let bearing = directionTo(getX(), getY(), bot.targetX, bot.targetY)
|
|
|
|
|
let delta = calcDeltaAngle(bearing, getGunDirection())
|
|
|
|
|
setGunTurnRate(delta)
|
|
|
|
|
let p = powerList[bot.powerIdx mod powerList.len]
|
|
|
|
|
if abs(delta) < 1.0 and getGunHeat() <= 0.0 and getEnergy() > p:
|
|
|
|
|
if setFire(p):
|
|
|
|
|
logRow(%*{"ev": "firecmd", "t": t, "p": p, "heatBefore": getGunHeat()})
|
|
|
|
|
inc bot.powerIdx
|
|
|
|
|
go()
|
|
|
|
|
|
|
|
|
|
when isMainModule:
|
|
|
|
|
var bot = RulesProbe()
|
|
|
|
|
start(bot, botJsonPath)
|