From cc3c2649c287719baafb8c005454a32a6cad95a4 Mon Sep 17 00:00:00 2001 From: Davide Cappellini Date: Wed, 16 Sep 2026 23:10:04 +0200 Subject: [PATCH] feat(SNNBot): thermometer speed encoding for precise lead discrimination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3-bit speed encoding couldn't distinguish speed 3 from speed 5 — 9° of lead error baked into the input. Thermometer coding with 8 bits gives 1-bit Hamming distance between adjacent speeds. MAX_K back to 128. Co-Authored-By: Claude Sonnet 4.6 --- SNNBot_garage/src/SNNBot.nim | 22 ++++++++++++---------- SNNBot_garage/src/reservoir.nim | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/SNNBot_garage/src/SNNBot.nim b/SNNBot_garage/src/SNNBot.nim index 306d51f..99adee5 100644 --- a/SNNBot_garage/src/SNNBot.nim +++ b/SNNBot_garage/src/SNNBot.nim @@ -1,6 +1,6 @@ # SNNBot — SNN core + aiming loop prototype (issue #152). # 80-input population-coded → 12 LIF hidden → polar-coded (sin/cos) output decoded via atan2. -# Inputs: relVelDir [0-35], speed [36-38], distance thermometer [39-46]. Raw bearing removed (irrelevant to lead). +# Inputs: relVelDir [0-35], speed thermometer [36-43], distance thermometer [44-51]. Raw bearing removed (irrelevant to lead). # No movement, no firing. SuperSpike three-factor rule on all weights (issue #158). # Binary reservoir alternative toggled via USE_RESERVOIR const (issue #159). # @@ -325,9 +325,10 @@ proc toBinaryInput(bearing: float, velDir: float, velSpeed: float, distance: flo ## Physics-based encoding for lead generalization (issue #152). ## Raw bearing removed — irrelevant to lead offset. ## Bits 0-35: relVelDir = (velDir - bearing) normalized to [-180,180), 36 bins × 10°, 5 active (center ± 2 neighbors). - ## Bits 36-38: speed level (3 bits, center ± 1 neighbor). - ## Bits 39-46: distance thermometer, 8 bands of ~125px in [0,1000]. - ## Bits 47-79: unused (sparse is fine for popcount similarity). + ## Bits 36-43: speed thermometer (8 bits): speedInt bits set from bit 36 upward, 0-8 active. + ## Adjacent speeds differ by 1 bit (Hamming-1), giving smooth generalization. + ## Bits 44-51: distance thermometer, 8 bands of ~125px in [0,1000]. + ## Bits 52-79: unused. ## ponytail: physics-based; revert to absolute bearing if convergence regresses result = [0'u64, 0'u64] @@ -342,17 +343,18 @@ proc toBinaryInput(bearing: float, velDir: float, velSpeed: float, distance: flo let idx = (vCenter + offset + 36) mod 36 # bits 0-35 result[idx div 64] = result[idx div 64] or (1'u64 shl (idx mod 64)) - # Speed: bits 36-38, center ± 1 neighbor clamped to [0,2] - let sCenter = clamp(int(velSpeed), 0, 2) - for offset in -1 .. 1: - let idx = 36 + clamp(sCenter + offset, 0, 2) + # Speed thermometer: bits 36-43; set bits 36..(36+speedInt-1) + # speedInt=0 → no bits; speedInt=8 → all 8 bits set + let speedInt = clamp(int(velSpeed + 0.5), 0, 8) + for b in 0 ..< speedInt: + let idx = 36 + b result[idx div 64] = result[idx div 64] or (1'u64 shl (idx mod 64)) - # Distance thermometer: bits 39-46, 8 bands of ~125px over [0,1000] + # Distance thermometer: bits 44-51, 8 bands of ~125px over [0,1000] # All bits up to the current band set (inclusive) let band = clamp(int(distance / 125.0), 0, 7) for b in 0 .. band: - let idx = 39 + b + let idx = 44 + b result[idx div 64] = result[idx div 64] or (1'u64 shl (idx mod 64)) # ── Main loop ───────────────────────────────────────────────────────────────── diff --git a/SNNBot_garage/src/reservoir.nim b/SNNBot_garage/src/reservoir.nim index eb7c305..926439e 100644 --- a/SNNBot_garage/src/reservoir.nim +++ b/SNNBot_garage/src/reservoir.nim @@ -5,7 +5,7 @@ import std/[bitops, math] const INPUT_BITS* = 80 WORDS_IN* = 2 # 80 bits → 2 × uint64 (128 bits, only 80 used) - MAX_K* = 64 + MAX_K* = 128 MIN_SIM* = 3 type