GridQuest
What it measures
GridQuest scores a human input method the same way a BCI decoder gets scored: achieved bit rate, after Shenoy et al. 2021 —
B = log2(N - 1) * max(S_c - S_i, 0) / t
With N = 81 possible targets (a 9×9 grid), a correct selection carries
log2(80) ≈ 6.32 bits. The point isn’t the game — it’s using it as a
benchmark for how much information a real selection interface can move
per second, comparable against neural-decoding baselines.
The selection mechanic
Each target is chosen with exactly two keypresses. A cue shows a mini 9×9
board with one cell highlighted; the player presses the key for the outer
3×3 group containing it, then the key for the inner position within that
group. Q W E / A S D / Z X C map spatially to both the outer grouping
and the inner position, so the same nine keys carry both stages of the
selection and the motor vocabulary stays tiny. Cues are drawn i.i.d.
uniform — no language model, no letter frequencies, no locale bias — so
the paradigm generalizes past any one alphabet.
Try it
A from-scratch JS/DOM port of the same session state machine, selection
logic, and bit-rate formula — not the Pygame binary itself (that can’t run
in a browser), but exactly the rules described above. Click the board or
Start practice, then use Q W E / A S D / Z X C on your keyboard (tile
clicks work too). Start scored run runs the real 60-second timed
benchmark, countdown included.
Architecture
src/bitrate_game/
core.py Session, BitRateTracker, TargetSource (UI-free logic)
mode.py GridQuestMode + GameMode protocol
adapters.py PygameKeyboardAdapter + InputAdapter protocol
renderer.py PygameGridRenderer + Renderer protocol
config.py all tunables (grid size, keys, timing, colors)
main.py wires the components together
core and mode hold zero pygame imports. Input handling and rendering
sit behind InputAdapter/Renderer protocols, so swapping in a browser
front end or a different input device (e.g. an actual BCI decoder feeding
selections in) means writing a new adapter/renderer pair, not touching
the scoring or session logic.
Shipping it
Standalone binaries are built per-OS with Nuitka through a GitHub Actions build matrix — Nuitka doesn’t cross-compile, so each target OS builds its own binary rather than being cross-built from one runner. Prefer the real thing over the in-browser port above? Download a native build for macOS, Windows, or Linux from the releases page.