feat(BNNBot): 44-bit encoding with sin/cos bearing+heading, distance%, velocity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-17 22:47:04 +02:00
parent b31d1bf99f
commit 0f20e1a869
3 changed files with 47 additions and 31 deletions
Binary file not shown.
+1 -4
View File
@@ -1,5 +1,5 @@
# BNNBot — binary encoding data collector. # BNNBot — binary encoding data collector.
# Radar lock on enemy, converts each scan to a 50-bit BinaryVector, prints it. # Radar lock on enemy, converts each scan to a 44-bit BinaryVector, prints it.
# No aiming, no firing — pure scan visualization for BNN research. # No aiming, no firing — pure scan visualization for BNN research.
import std/[math, os] import std/[math, os]
@@ -18,7 +18,6 @@ type
hasLastPos: bool hasLastPos: bool
velocity: float # signed velocity (from heading + speed) velocity: float # signed velocity (from heading + speed)
heading: float # enemy heading (degrees) heading: float # enemy heading (degrees)
speed: float # enemy speed (units/tick)
distance: float distance: float
tick: int tick: int
@@ -27,7 +26,6 @@ method onScannedBot*(bot: BNNBot, e: ScannedBotEvent) =
bot.enemyBearing = directionTo(bx, by, e.x, e.y) bot.enemyBearing = directionTo(bx, by, e.x, e.y)
bot.distance = distanceTo(bx, by, e.x, e.y) bot.distance = distanceTo(bx, by, e.x, e.y)
bot.heading = e.direction bot.heading = e.direction
bot.speed = abs(e.speed)
bot.velocity = e.speed # signed: positive = forward, negative = reverse bot.velocity = e.speed # signed: positive = forward, negative = reverse
if bot.hasLastPos: if bot.hasLastPos:
discard # velocity from e.speed is more accurate; position delta kept for future use discard # velocity from e.speed is more accurate; position delta kept for future use
@@ -41,7 +39,6 @@ method onScannedBot*(bot: BNNBot, e: ScannedBotEvent) =
distance: bot.distance, distance: bot.distance,
velocity: bot.velocity, velocity: bot.velocity,
heading: bot.heading, heading: bot.heading,
speed: bot.speed,
) )
let vec = encode(scan) let vec = encode(scan)
echo $bot.tick, " ", formatBinary(vec) echo $bot.tick, " ", formatBinary(vec)
+46 -27
View File
@@ -1,42 +1,61 @@
## binary_encoding — scan data → binary vector (10-bit per value).
import std/math import std/math
const const
BITS_PER_VALUE* = 10 TOTAL_BITS* = 44
NUM_VALUES* = 5
TOTAL_BITS* = BITS_PER_VALUE * NUM_VALUES # 50
type type
BinaryVector* = array[TOTAL_BITS, uint8] BinaryVector* = array[TOTAL_BITS, uint8]
ScanData* = object ScanData* = object
bearing*: float # 0-360 bearing*: float # 0-360 degrees
distance*: float # 0-2000 distance*: float # 0-1414 pixels (will be converted to 0-100%)
velocity*: float # -8 to +8 velocity*: float # -8 to +8 integer steps
heading*: float # 0-360 heading*: float # 0-360 degrees
speed*: float # 0-8
proc toBits(value: float, minVal, maxVal: float): array[BITS_PER_VALUE, uint8] = const MAX_DISTANCE = 1414.0 # diagonal of 1000x1000 arena
let clamped = clamp(value, minVal, maxVal)
let norm = (clamped - minVal) / (maxVal - minVal) proc toBits(value: int, bits: int): seq[uint8] =
let intVal = int(norm * 1023.0) # 0-1023 fits in 10 bits result = newSeq[uint8](bits)
for i in 0..<BITS_PER_VALUE: for i in 0..<bits:
result[BITS_PER_VALUE - 1 - i] = uint8((intVal shr i) and 1) # MSB first result[bits - 1 - i] = uint8((value shr i) and 1)
proc encode*(scan: ScanData): BinaryVector = proc encode*(scan: ScanData): BinaryVector =
let parts = [
toBits(scan.bearing, 0.0, 360.0),
toBits(scan.distance, 0.0, 2000.0),
toBits(scan.velocity, -8.0, 8.0),
toBits(scan.heading, 0.0, 360.0),
toBits(scan.speed, 0.0, 8.0)
]
var offset = 0 var offset = 0
for p in parts:
for i in 0..<BITS_PER_VALUE: # 1. bearing_sin: sin(bearing), -1..+1 → 0..199 → 8 bits
result[offset + i] = p[i] let bSin = int((sin(degToRad(scan.bearing)) + 1.0) / 2.0 * 199.0)
offset += BITS_PER_VALUE let bSinBits = toBits(clamp(bSin, 0, 199), 8)
for i in 0..<8: result[offset + i] = bSinBits[i]
offset += 8
# 2. bearing_cos: cos(bearing), -1..+1 → 0..199 → 8 bits
let bCos = int((cos(degToRad(scan.bearing)) + 1.0) / 2.0 * 199.0)
let bCosBits = toBits(clamp(bCos, 0, 199), 8)
for i in 0..<8: result[offset + i] = bCosBits[i]
offset += 8
# 3. distance %: 0-100% of max → 0..99 → 7 bits
let distPct = int(clamp(scan.distance / MAX_DISTANCE * 99.0, 0.0, 99.0))
let distBits = toBits(distPct, 7)
for i in 0..<7: result[offset + i] = distBits[i]
offset += 7
# 4. velocity: -8..+8 → 0..16 → 5 bits
let vel = int(clamp(scan.velocity + 8.0, 0.0, 16.0))
let velBits = toBits(vel, 5)
for i in 0..<5: result[offset + i] = velBits[i]
offset += 5
# 5. heading_sin: sin(heading), -1..+1 → 0..199 → 8 bits
let hSin = int((sin(degToRad(scan.heading)) + 1.0) / 2.0 * 199.0)
let hSinBits = toBits(clamp(hSin, 0, 199), 8)
for i in 0..<8: result[offset + i] = hSinBits[i]
offset += 8
# 6. heading_cos: cos(heading), -1..+1 → 0..199 → 8 bits
let hCos = int((cos(degToRad(scan.heading)) + 1.0) / 2.0 * 199.0)
let hCosBits = toBits(clamp(hCos, 0, 199), 8)
for i in 0..<8: result[offset + i] = hCosBits[i]
proc formatBinary*(vec: BinaryVector): string = proc formatBinary*(vec: BinaryVector): string =
for b in vec: for b in vec: