simplify(BNNBot): raw 10-bit binary encoding, clean output
- Replace population/thermometer/signed-thermometer encoding with simple 10-bit binary per value - 5 values × 10 bits = 50 bits total (was 112) - Each value normalized to 0-1023, then bit-extracted MSB-first - Output is now just tick and raw binary string (no field labels, no debug log) - Remove file logging entirely; stdout only Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,22 +1,13 @@
|
||||
# BNNBot — binary encoding data collector.
|
||||
# Radar lock on enemy, converts each scan to a 112-bit BinaryVector, logs + prints it.
|
||||
# Radar lock on enemy, converts each scan to a 50-bit BinaryVector, prints it.
|
||||
# No aiming, no firing — pure scan visualization for BNN research.
|
||||
|
||||
import std/[math, strformat, os]
|
||||
import std/[math, os]
|
||||
import robocode_tankroyale_botapi
|
||||
import radar_lock/radar_lock as radar_lock
|
||||
import binary_encoding
|
||||
|
||||
const botJsonPath = currentSourcePath().parentDir / "BNNBot.json"
|
||||
const scanLog = "/tmp/bnnbot_scan.log"
|
||||
|
||||
proc appendLog(s: string) =
|
||||
try:
|
||||
let f = open(scanLog, fmAppend)
|
||||
f.writeLine(s)
|
||||
f.close()
|
||||
except:
|
||||
discard
|
||||
|
||||
type
|
||||
BNNBot = ref object of Bot
|
||||
@@ -53,10 +44,7 @@ method onScannedBot*(bot: BNNBot, e: ScannedBotEvent) =
|
||||
speed: bot.speed,
|
||||
)
|
||||
let vec = encode(scan)
|
||||
let fmt = formatBinary(vec)
|
||||
let line = fmt"t={bot.tick} bear={bot.enemyBearing:.1f} dist={bot.distance:.0f} vel={bot.velocity:.2f} head={bot.heading:.1f} spd={bot.speed:.2f} pop={popcount(vec)} {fmt}"
|
||||
echo line
|
||||
appendLog(line)
|
||||
echo $bot.tick, " ", formatBinary(vec)
|
||||
|
||||
method onRoundStarted*(bot: BNNBot, e: RoundStartedEvent) =
|
||||
setAdjustGunForBodyTurn(true)
|
||||
@@ -70,12 +58,7 @@ method onRoundStarted*(bot: BNNBot, e: RoundStartedEvent) =
|
||||
setTurnRate(0.0)
|
||||
|
||||
method onGameStarted*(bot: BNNBot, e: GameStartedEventForBot) =
|
||||
# Truncate log on game start
|
||||
try:
|
||||
let f = open(scanLog, fmWrite)
|
||||
f.close()
|
||||
except:
|
||||
discard
|
||||
discard
|
||||
|
||||
method run*(bot: BNNBot) =
|
||||
while isRunning():
|
||||
|
||||
@@ -1,127 +1,50 @@
|
||||
## binary_encoding — scan data → binary vector for BNNBot.
|
||||
## Encoding scheme:
|
||||
## bearing/heading: population code (2 adjacent bits, triangular interpolation)
|
||||
## distance/speed: thermometer (all bits below threshold active)
|
||||
## velocity: signed thermometer (first half = negative, second half = positive)
|
||||
## binary_encoding — scan data → binary vector (10-bit per value).
|
||||
|
||||
import std/math
|
||||
|
||||
const
|
||||
BEARING_BITS* = 36 # 360° / 10° per bit
|
||||
DISTANCE_BITS* = 16 # 0-2000 units, ~125 per bit (thermometer)
|
||||
VELOCITY_BITS* = 16 # -8 to +8, signed thermometer
|
||||
HEADING_BITS* = 36 # 360° / 10° per bit
|
||||
SPEED_BITS* = 8 # 0-8, 1 per bit (thermometer)
|
||||
TOTAL_BITS* = BEARING_BITS + DISTANCE_BITS + VELOCITY_BITS + HEADING_BITS + SPEED_BITS # 112
|
||||
BITS_PER_VALUE* = 10
|
||||
NUM_VALUES* = 5
|
||||
TOTAL_BITS* = BITS_PER_VALUE * NUM_VALUES # 50
|
||||
|
||||
type
|
||||
BinaryVector* = array[TOTAL_BITS, uint8] # 0 or 1
|
||||
BinaryVector* = array[TOTAL_BITS, uint8]
|
||||
|
||||
ScanData* = object
|
||||
bearing*: float # absolute bearing to enemy (degrees)
|
||||
distance*: float # distance to enemy
|
||||
velocity*: float # enemy velocity (negative = moving backward)
|
||||
heading*: float # enemy heading (degrees)
|
||||
speed*: float # enemy speed (always positive)
|
||||
bearing*: float # 0-360
|
||||
distance*: float # 0-2000
|
||||
velocity*: float # -8 to +8
|
||||
heading*: float # 0-360
|
||||
speed*: float # 0-8
|
||||
|
||||
proc encodeBearing*(degrees: float, bits: int): seq[uint8] =
|
||||
## Population code: 2 adjacent bits active via triangular interpolation.
|
||||
result = newSeq[uint8](bits)
|
||||
let norm = ((degrees mod 360.0) + 360.0) mod 360.0
|
||||
let bandWidth = 360.0 / float(bits)
|
||||
let idx = norm / bandWidth
|
||||
let lo = int(idx) mod bits
|
||||
let hi = (lo + 1) mod bits
|
||||
result[lo] = 1
|
||||
result[hi] = 1
|
||||
|
||||
proc encodeThermometer*(value, minVal, maxVal: float, bits: int): seq[uint8] =
|
||||
## Thermometer code: all bits below threshold are 1.
|
||||
result = newSeq[uint8](bits)
|
||||
proc toBits(value: float, minVal, maxVal: float): array[BITS_PER_VALUE, uint8] =
|
||||
let clamped = clamp(value, minVal, maxVal)
|
||||
let norm = (clamped - minVal) / (maxVal - minVal)
|
||||
let active = int(norm * float(bits))
|
||||
for i in 0..<active:
|
||||
result[i] = 1
|
||||
|
||||
proc encodeSignedThermometer*(value, maxAbs: float, bits: int): seq[uint8] =
|
||||
## Signed thermometer: first half = negative magnitude, second half = positive magnitude.
|
||||
result = newSeq[uint8](bits)
|
||||
let halfBits = bits div 2
|
||||
let clamped = clamp(value, -maxAbs, maxAbs)
|
||||
if clamped >= 0:
|
||||
let norm = clamped / maxAbs
|
||||
let active = int(norm * float(halfBits))
|
||||
for i in 0..<active:
|
||||
result[halfBits + i] = 1
|
||||
else:
|
||||
let norm = -clamped / maxAbs
|
||||
let active = int(norm * float(halfBits))
|
||||
for i in countdown(halfBits - 1, halfBits - active):
|
||||
result[i] = 1
|
||||
let intVal = int(norm * 1023.0) # 0-1023 fits in 10 bits
|
||||
for i in 0..<BITS_PER_VALUE:
|
||||
result[BITS_PER_VALUE - 1 - i] = uint8((intVal shr i) and 1) # MSB first
|
||||
|
||||
proc encode*(scan: ScanData): BinaryVector =
|
||||
## Encode full scan data into a 112-bit binary vector.
|
||||
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
|
||||
|
||||
let bearBits = encodeBearing(scan.bearing, BEARING_BITS)
|
||||
for i in 0..<BEARING_BITS:
|
||||
result[offset + i] = bearBits[i]
|
||||
offset += BEARING_BITS
|
||||
|
||||
let distBits = encodeThermometer(scan.distance, 0.0, 2000.0, DISTANCE_BITS)
|
||||
for i in 0..<DISTANCE_BITS:
|
||||
result[offset + i] = distBits[i]
|
||||
offset += DISTANCE_BITS
|
||||
|
||||
let velBits = encodeSignedThermometer(scan.velocity, 8.0, VELOCITY_BITS)
|
||||
for i in 0..<VELOCITY_BITS:
|
||||
result[offset + i] = velBits[i]
|
||||
offset += VELOCITY_BITS
|
||||
|
||||
let headBits = encodeBearing(scan.heading, HEADING_BITS)
|
||||
for i in 0..<HEADING_BITS:
|
||||
result[offset + i] = headBits[i]
|
||||
offset += HEADING_BITS
|
||||
|
||||
let spdBits = encodeThermometer(scan.speed, 0.0, 8.0, SPEED_BITS)
|
||||
for i in 0..<SPEED_BITS:
|
||||
result[offset + i] = spdBits[i]
|
||||
for p in parts:
|
||||
for i in 0..<BITS_PER_VALUE:
|
||||
result[offset + i] = p[i]
|
||||
offset += BITS_PER_VALUE
|
||||
|
||||
proc formatBinary*(vec: BinaryVector): string =
|
||||
## Pretty-print binary vector with labeled sections.
|
||||
result = "BIN ["
|
||||
result &= "B:"
|
||||
for i in 0..<BEARING_BITS:
|
||||
result &= (if vec[i] == 1: "█" else: "░")
|
||||
result &= "|"
|
||||
var offset = BEARING_BITS
|
||||
result &= "D:"
|
||||
for i in 0..<DISTANCE_BITS:
|
||||
result &= (if vec[offset + i] == 1: "█" else: "░")
|
||||
result &= "|"
|
||||
offset += DISTANCE_BITS
|
||||
result &= "V:"
|
||||
for i in 0..<VELOCITY_BITS:
|
||||
result &= (if vec[offset + i] == 1: "█" else: "░")
|
||||
result &= "|"
|
||||
offset += VELOCITY_BITS
|
||||
result &= "H:"
|
||||
for i in 0..<HEADING_BITS:
|
||||
result &= (if vec[offset + i] == 1: "█" else: "░")
|
||||
result &= "|"
|
||||
offset += HEADING_BITS
|
||||
result &= "S:"
|
||||
for i in 0..<SPEED_BITS:
|
||||
result &= (if vec[offset + i] == 1: "█" else: "░")
|
||||
result &= "]"
|
||||
for b in vec:
|
||||
result &= (if b == 1: "1" else: "0")
|
||||
|
||||
proc popcount*(vec: BinaryVector): int =
|
||||
## Count active bits.
|
||||
for b in vec:
|
||||
result += int(b)
|
||||
for b in vec: result += int(b)
|
||||
|
||||
proc hammingDistance*(a, b: BinaryVector): int =
|
||||
## XOR + popcount.
|
||||
for i in 0..<TOTAL_BITS:
|
||||
result += int(a[i] xor b[i])
|
||||
|
||||
Reference in New Issue
Block a user