From 50e3c6fae96bc953fe6fdd678142a67a1c361bb5 Mon Sep 17 00:00:00 2001 From: Davide Cappellini Date: Thu, 17 Sep 2026 23:35:57 +0200 Subject: [PATCH] =?UTF-8?q?feat(BNNBot):=2010-tick=20sliding=20window=20of?= =?UTF-8?q?=20enemy=20data=20+=20self=20state=20=E2=80=94=20870=20bits=20t?= =?UTF-8?q?otal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- BNNBot_garage/src/BNNBot.nim | 100 ++++++++++++++---------- BNNBot_garage/src/binary_encoding.nim | 106 +++++++++++++++----------- 2 files changed, 122 insertions(+), 84 deletions(-) diff --git a/BNNBot_garage/src/BNNBot.nim b/BNNBot_garage/src/BNNBot.nim index 6592e7f..2c21e3f 100644 --- a/BNNBot_garage/src/BNNBot.nim +++ b/BNNBot_garage/src/BNNBot.nim @@ -1,5 +1,5 @@ # BNNBot — binary encoding data collector. -# Radar lock on enemy, converts each scan to a 44-bit BinaryVector, prints it. +# Radar lock on enemy, encodes a 10-tick sliding window (870 bits), prints it. # No aiming, no firing — pure scan visualization for BNN research. import std/[math, os, strutils] @@ -11,56 +11,73 @@ const botJsonPath = currentSourcePath().parentDir / "BNNBot.json" type BNNBot = ref object of Bot - hasContact: bool + hasContact: bool enemyBearing: float - lastEnemyX: float - lastEnemyY: float - hasLastPos: bool - velocity: float # signed velocity (from heading + speed) - heading: float # enemy heading (degrees) - distance: float - tick: int - prevVec: BinaryVector - hasPrev: bool + lastEnemyX: float + lastEnemyY: float + hasLastPos: bool + velocity: float + heading: float + distance: float + tick: int + prevVec: BinaryVector + hasPrev: bool + frameBuffer: array[WINDOW_SIZE, array[FRAME_BITS, uint8]] + bufferCount: int method onScannedBot*(bot: BNNBot, e: ScannedBotEvent) = let bx = getX(); let by = getY() bot.enemyBearing = directionTo(bx, by, e.x, e.y) bot.distance = distanceTo(bx, by, e.x, e.y) bot.heading = e.direction - bot.velocity = e.speed # signed: positive = forward, negative = reverse - if bot.hasLastPos: - discard # velocity from e.speed is more accurate; position delta kept for future use - bot.lastEnemyX = e.x - bot.lastEnemyY = e.y - bot.hasLastPos = true - bot.hasContact = true + bot.velocity = e.speed + bot.lastEnemyX = e.x + bot.lastEnemyY = e.y + bot.hasLastPos = true + bot.hasContact = true - let arenaW = getArenaWidth().float - let arenaH = getArenaHeight().float - let enemyX = e.x - let enemyY = e.y - let scan = ScanData( - bearing: bot.enemyBearing, - distance: bot.distance, - velocity: bot.velocity, - heading: bot.heading, - myWallN: arenaH - by, - myWallS: by, - myWallE: arenaW - bx, - myWallW: bx, - enemyWallN: arenaH - enemyY, - enemyWallS: enemyY, - enemyWallE: arenaW - enemyX, - enemyWallW: enemyX, - myEnergy: getEnergy(), - enemyEnergy: e.energy, - canFire: getGunHeat() <= 0.0, + let arenaW = getArenaWidth().float + let arenaH = getArenaHeight().float + let enemyX = e.x + let enemyY = e.y + + let frame = EnemyScanFrame( + bearing: bot.enemyBearing, + distance: bot.distance, + velocity: bot.velocity, + heading: bot.heading, + enemyWallN: arenaH - enemyY, + enemyWallS: enemyY, + enemyWallE: arenaW - enemyX, + enemyWallW: enemyX, + enemyEnergy: e.energy, ) - let vec = encode(scan) - let hamming = if bot.hasPrev: hammingDistance(bot.prevVec, vec) else: 0 + let encoded = encodeFrame(frame) + + # Shift buffer: 0..8 → 1..9, newest at index 0 + for i in countdown(WINDOW_SIZE - 1, 1): + bot.frameBuffer[i] = bot.frameBuffer[i - 1] + bot.frameBuffer[0] = encoded + + if bot.bufferCount < WINDOW_SIZE: + inc bot.bufferCount + if bot.bufferCount < WINDOW_SIZE: + return # still filling + + let selfState = SelfState( + myWallN: arenaH - by, + myWallS: by, + myWallE: arenaW - bx, + myWallW: bx, + myEnergy: getEnergy(), + canFire: getGunHeat() <= 0.0, + ) + let selfEncoded = encodeSelf(selfState) + let vec = encodeFullVector(bot.frameBuffer, selfEncoded) + + let hamming = if bot.hasPrev: hammingDistance(bot.prevVec, vec) else: 0 let similarity = if bot.hasPrev: TOTAL_BITS - hamming else: 0 - let overlap = if bot.hasPrev: popcount(bitwiseAnd(bot.prevVec, vec)) else: 0 + let overlap = if bot.hasPrev: popcount(bitwiseAnd(bot.prevVec, vec)) else: 0 echo align($bot.tick, 4), " ", formatBinary(vec), " ", hamming, " ", similarity, " ", overlap bot.prevVec = vec bot.hasPrev = true @@ -74,6 +91,7 @@ method onRoundStarted*(bot: BNNBot, e: RoundStartedEvent) = bot.hasLastPos = false bot.tick = 0 bot.hasPrev = false + bot.bufferCount = 0 setTargetSpeed(0.0) setTurnRate(0.0) diff --git a/BNNBot_garage/src/binary_encoding.nim b/BNNBot_garage/src/binary_encoding.nim index 6c80030..f46c619 100644 --- a/BNNBot_garage/src/binary_encoding.nim +++ b/BNNBot_garage/src/binary_encoding.nim @@ -1,27 +1,32 @@ import std/math const - TOTAL_BITS* = 123 + FRAME_BITS* = 83 + SELF_BITS* = 40 + WINDOW_SIZE* = 10 + TOTAL_BITS* = FRAME_BITS * WINDOW_SIZE + SELF_BITS # 870 type BinaryVector* = array[TOTAL_BITS, uint8] - ScanData* = object - bearing*: float # 0-360 degrees - distance*: float # 0-1414 pixels (will be converted to 0-100%) - velocity*: float # -8 to +8 integer steps - heading*: float # 0-360 degrees - myWallN*: float # our distance to north wall - myWallS*: float # our distance to south wall - myWallE*: float # our distance to east wall - myWallW*: float # our distance to west wall - enemyWallN*: float # enemy distance to north wall - enemyWallS*: float # enemy distance to south wall - enemyWallE*: float # enemy distance to east wall - enemyWallW*: float # enemy distance to west wall - myEnergy*: float # 0-150 - enemyEnergy*: float # 0-150 - canFire*: bool # gun heat == 0 + EnemyScanFrame* = object + bearing*: float # → sin/cos, 8+8 bits + distance*: float # → %, 7 bits + velocity*: float # → 5 bits + heading*: float # → sin/cos, 8+8 bits + enemyWallN*: float # 7 bits + enemyWallS*: float # 7 bits + enemyWallE*: float # 7 bits + enemyWallW*: float # 7 bits + enemyEnergy*: float # 11 bits + + SelfState* = object + myWallN*: float # 7 bits + myWallS*: float # 7 bits + myWallE*: float # 7 bits + myWallW*: float # 7 bits + myEnergy*: float # 11 bits + canFire*: bool # 1 bit const MAX_DISTANCE = 1414.0 # diagonal of 1000x1000 arena @@ -34,73 +39,88 @@ proc toBits(value: int, bits: int): seq[uint8] = for i in 0..