feat(ModularBot): melee targeting — multi-enemy tracker, per-enemy gun fitness, radar auto-switch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
## Per-enemy state table. Keyed by bot ID.
|
||||
## Tank Royale: 0° = East.
|
||||
|
||||
import std/tables
|
||||
import std/math
|
||||
|
||||
type
|
||||
EnemyState* = object
|
||||
id*: int
|
||||
x*, y*: float
|
||||
heading*: float ## degrees
|
||||
speed*: float
|
||||
energy*: float
|
||||
lastSeenTick*: int
|
||||
alive*: bool
|
||||
|
||||
EnemyTracker* = object
|
||||
enemies*: Table[int, EnemyState]
|
||||
|
||||
proc update*(et: var EnemyTracker, botId: int, x, y, heading, speed, energy: float, tick: int) =
|
||||
et.enemies[botId] = EnemyState(id: botId, x: x, y: y, heading: heading,
|
||||
speed: speed, energy: energy,
|
||||
lastSeenTick: tick, alive: true)
|
||||
|
||||
proc markDead*(et: var EnemyTracker, botId: int) =
|
||||
if botId in et.enemies:
|
||||
et.enemies[botId].alive = false
|
||||
|
||||
proc resetRound*(et: var EnemyTracker) =
|
||||
et.enemies.clear()
|
||||
|
||||
proc getEnemy*(et: EnemyTracker, botId: int): EnemyState =
|
||||
et.enemies[botId]
|
||||
|
||||
proc allAlive*(et: EnemyTracker): seq[EnemyState] =
|
||||
for s in et.enemies.values:
|
||||
if s.alive: result.add(s)
|
||||
|
||||
proc closestTo*(et: EnemyTracker, x, y: float): EnemyState =
|
||||
var bestDist = Inf
|
||||
for s in et.enemies.values:
|
||||
if not s.alive: continue
|
||||
let d = hypot(s.x - x, s.y - y)
|
||||
if d < bestDist:
|
||||
bestDist = d
|
||||
result = s
|
||||
|
||||
proc weakest*(et: EnemyTracker): EnemyState =
|
||||
var bestEnergy = Inf
|
||||
for s in et.enemies.values:
|
||||
if not s.alive: continue
|
||||
if s.energy < bestEnergy:
|
||||
bestEnergy = s.energy
|
||||
result = s
|
||||
@@ -0,0 +1,19 @@
|
||||
## Target selector: picks which enemy to aim at.
|
||||
|
||||
import enemy_tracker
|
||||
|
||||
type TargetMode* = enum
|
||||
tmClosest, ## nearest enemy
|
||||
tmWeakest, ## lowest energy (finisher)
|
||||
tmBestGun ## future — alias to closest for now
|
||||
|
||||
proc selectTarget*(et: EnemyTracker, selfX, selfY: float,
|
||||
mode: TargetMode = tmClosest): int =
|
||||
## Returns enemy bot ID. Falls back to 1v1 naturally when 1 alive.
|
||||
let alive = et.allAlive()
|
||||
if alive.len == 0: return -1
|
||||
if alive.len == 1: return alive[0].id
|
||||
let chosen = case mode
|
||||
of tmWeakest: et.weakest()
|
||||
else: et.closestTo(selfX, selfY) # tmClosest + tmBestGun
|
||||
result = chosen.id
|
||||
Reference in New Issue
Block a user