# ponytail: grid accumulator replaces ring buffer; add temporal features when needed (step 3) import std/math const VPERP_BINS* = 17 # -8 to +8 inclusive (integer speed units) DIST_BINS* = 8 # distance bands DIST_BAND* = 125.0 # pixels per band type GridCell = object sumSin: float sumCos: float count: float LeadGrid* = object cells: array[VPERP_BINS * DIST_BINS, GridCell] # 17 × 8 = 136 cells const BULLET_SPEED = 14.0 # power 2 bullet speed in pixels/tick proc initLeadGrid*(): LeadGrid = for vBin in 0..= VPERP_BINS or db < 0 or db >= DIST_BINS: continue let c = grid.cells[vb * DIST_BINS + db] if c.count < 0.001: continue let w = if dv == 0 and dd == 0: 1.0 elif dv == 0 or dd == 0: 0.5 else: 0.25 sinSum += w * c.sumSin / c.count cosSum += w * c.sumCos / c.count totalW += w if totalW == 0.0: return -999.0 result = radToDeg(arctan2(sinSum, cosSum)) proc learn*(grid: var LeadGrid, vPerp, distance, correctOffset: float) = let idx = cellIndex(vPerp, distance) grid.cells[idx].sumSin += sin(degToRad(correctOffset)) grid.cells[idx].sumCos += cos(degToRad(correctOffset)) grid.cells[idx].count += 1.0 proc totalCount*(grid: LeadGrid): float = for c in grid.cells: result += c.count