onlyn00bs-badge

OnlyN00bs: a DEF CON 34 friend-finder badge. ESP32 firmware, Web Bluetooth setup app, printable case
git clone https://git.virtualshack.io/onlyn00bs-badge.git
Log | Files | Refs | README | LICENSE

points.h (3613B)


      1 #pragma once
      2 #include <Arduino.h>
      3 #include <Preferences.h>
      4 #include <string.h>
      5 #include "config.h"
      6 
      7 // ─────────────────────────────────────────────────────────────────────────
      8 //  Points ledger — per-friend attribution of your proximity score.
      9 //
     10 //  The global points tally (main.cpp) counts one point per minute spent with
     11 //  crew close enough to clear the RSSI gate. This ledger records WHO you shared
     12 //  those minutes with: each earning tick is credited to EVERY in-gate friend.
     13 //  So a friend's seconds = how many of your social minutes they were part of
     14 //  (each ≤ the tally, but they overlap — a minute spent in a group of three
     15 //  credits all three). "Best Friends" ranks the top sharers, most first.
     16 //
     17 //  Bounded (crew is small) and main-loop-only — no locking needed, unlike the
     18 //  peer table. Persisted to NVS as a blob so a reboot mid-con keeps the ranking.
     19 // ─────────────────────────────────────────────────────────────────────────
     20 
     21 #ifndef MAX_POINT_FRIENDS
     22 #define MAX_POINT_FRIENDS  24       // > expected crew size; evicts the smallest
     23 #endif
     24 
     25 struct PointFriend {
     26   char     handle[HANDLE_MAX_LEN + 1];
     27   uint32_t seconds;                 // gated proximity time credited to this friend
     28 };
     29 
     30 class PointsLedger {
     31 public:
     32   // Credit `secs` of proximity to `handle` (the closest crew this tick).
     33   void credit(const char* handle, uint32_t secs) {
     34     if (!secs || !handle || !handle[0]) return;
     35     int idx = find(handle);
     36     if (idx < 0) {
     37       idx = (_n < MAX_POINT_FRIENDS) ? _n++ : smallest();
     38       memset(&_f[idx], 0, sizeof(PointFriend));
     39       strncpy(_f[idx].handle, handle, HANDLE_MAX_LEN);
     40       _f[idx].handle[HANDLE_MAX_LEN] = '\0';
     41     }
     42     _f[idx].seconds += secs;
     43   }
     44 
     45   uint32_t total() const {
     46     uint32_t s = 0;
     47     for (int i = 0; i < _n; i++) s += _f[i].seconds;
     48     return s;
     49   }
     50 
     51   // Copy entries into `out` sorted by contribution (most first); returns the
     52   // number copied (≤ max). `out` must hold up to MAX_POINT_FRIENDS.
     53   int top(PointFriend* out, int max) const {
     54     for (int i = 0; i < _n; i++) out[i] = _f[i];
     55     for (int i = 1; i < _n; i++) {            // insertion sort, n is tiny
     56       PointFriend key = out[i];
     57       int j = i - 1;
     58       while (j >= 0 && out[j].seconds < key.seconds) { out[j + 1] = out[j]; j--; }
     59       out[j + 1] = key;
     60     }
     61     return (_n < max) ? _n : max;
     62   }
     63 
     64   void save(Preferences& p) const {
     65     p.putUInt("pts_n", (uint32_t)_n);
     66     if (_n > 0) p.putBytes("pts", _f, (size_t)_n * sizeof(PointFriend));
     67   }
     68 
     69   void load(Preferences& p) {
     70     memset(_f, 0, sizeof(_f));
     71     uint32_t n = p.getUInt("pts_n", 0);
     72     if (n == 0 || n > MAX_POINT_FRIENDS) { _n = 0; return; }
     73     size_t want = (size_t)n * sizeof(PointFriend);
     74     size_t got  = p.getBytes("pts", _f, want);
     75     _n = (got == want) ? (int)n : 0;          // partial/missing blob → start clean
     76     for (int i = 0; i < _n; i++) _f[i].handle[HANDLE_MAX_LEN] = '\0';
     77   }
     78 
     79 private:
     80   PointFriend _f[MAX_POINT_FRIENDS];
     81   int         _n = 0;
     82 
     83   int find(const char* handle) const {
     84     for (int i = 0; i < _n; i++)
     85       if (strncmp(_f[i].handle, handle, HANDLE_MAX_LEN) == 0) return i;
     86     return -1;
     87   }
     88 
     89   int smallest() const {
     90     int idx = 0;
     91     for (int i = 1; i < _n; i++)
     92       if (_f[i].seconds < _f[idx].seconds) idx = i;
     93     return idx;
     94   }
     95 };