diff --git a/SNNBot_garage/src/reservoir.nim b/SNNBot_garage/src/reservoir.nim index e3a0361..16d6775 100644 --- a/SNNBot_garage/src/reservoir.nim +++ b/SNNBot_garage/src/reservoir.nim @@ -4,15 +4,15 @@ import std/[bitops, math] const INPUT_BITS* = 80 - N_BINS* = 72 - BIN_WIDTH* = 5.0 + N_BINS* = 360 + BIN_WIDTH* = 1.0 WORDS_IN* = 2 # 80 bits → 2 × uint64 (128 bits, only 80 used) type BitVec80* = array[WORDS_IN, uint64] BinaryAimer* = object - readout*: array[N_BINS, BitVec80] # 72 bins × 80-bit weight vectors + readout*: array[N_BINS, BitVec80] # 360 bins × 80-bit weight vectors scores*: array[N_BINS, int] proc initAimer*(): BinaryAimer = @@ -59,22 +59,24 @@ proc interpolatedAngle*(a: BinaryAimer, winnerBin: int): float = result = radToDeg(arctan2(sinAvg, cosAvg)) proc learn*(a: var BinaryAimer, input: BitVec80, correctAngle: float) = - ## WTA Hebbian: reinforce correct bin, punish worst wrong bin + ## WTA Hebbian: reinforce correct bin + ±2 neighbors, punish worst wrong bin (>2° away) let correctBin = angleToBin(correctAngle) - # Reinforce: OR input into correct bin - for w in 0 ..< WORDS_IN: - a.readout[correctBin][w] = a.readout[correctBin][w] or input[w] + # Reinforce correct bin + 2 neighbors on each side + for offset in -2 .. 2: + let bin = (correctBin + offset + N_BINS) mod N_BINS + for w in 0 ..< WORDS_IN: + a.readout[bin][w] = a.readout[bin][w] or input[w] - # Find highest-scoring WRONG bin + # Punish highest-scoring wrong bin (must be >2 bins away from correct) var worstBin = -1 var worstScore = -1 for k in 0 ..< N_BINS: - if k != correctBin and a.scores[k] > worstScore: + let dist = min(abs(k - correctBin), N_BINS - abs(k - correctBin)) # circular distance + if dist > 2 and a.scores[k] > worstScore: worstScore = a.scores[k] worstBin = k - # Punish: AND NOT input from worst wrong bin if worstBin >= 0: for w in 0 ..< WORDS_IN: a.readout[worstBin][w] = a.readout[worstBin][w] and (not input[w])