A simulated Drosophila melanogaster CNS (MaleCNS v1.0, 166,700 neurons / 25.6M retained connections) plays tic-tac-toe. The wiring is never modified; the only trained component is a linear readout of descending + VNC motor neuron spike rates, learned with self-play REINFORCE on a k3s GPU worker. A web app lets a human play against the trained fly brain, with live neural telemetry.
Method follows Alex Wormuth's DOOMFLY (the fly connectome playing Doom, driven by photoreceptor input, motor-neuron readout, PPL101 dopamine aversion) and flychess-hq.vercel.app (the same recipe for chess). This project reproduces the recipe for Connect-4 with real RL on the readout.
| Path | Contents |
|---|---|
flyc4/connectome.py |
MaleCNS v1.0 feather import -> CSR graph + task interface |
flyc4/env.py |
batched tic-tac-toe (int64 bitboards, 9 cells) |
flyc4/sim.py |
batched GPU LIF simulation (signed GABA synapses, homeostatic thresholds) |
flyc4/policy.py |
the trained linear readout + value baseline |
flyc4/train.py |
self-play REINFORCE loop (Job entrypoint) |
flyc4/eval.py |
batched evaluation vs random / minimax-depth-2 |
serve/app.py |
FastAPI play server (graph + readout on GPU) |
serve/static/ |
the playable UI |
deploy/ |
Dockerfile + k8s manifests (namespace, train Job, web Deployment) |
- Sensory in: own pieces drive 6-cell R1-R6 photoreceptor patches (9x6); opponent pieces drive 5-cell R8 patches (9x5). R cells are spread evenly across the retina (sorted-bodyId linspace). Position -> current, not spikes.
- Motor out: top 512 descending neurons + top 512 VNC motor neurons by outgoing synapses. Spike counts over a 96-tick decision window -> linear head -> masked softmax over the 9 cells.
- Dopamine: terminal losses schedule an aversive current into the PPL101 pair (doomfly-style). The gradient itself is REINFORCE + value baseline + entropy bonus.
- Dynamics: LIF with leak 0.85, signed synapse strengths (log1p(contact count) / sqrt(fan-out), GABA edges negative per the dataset's consensus neurotransmitter predictions) and per-neuron homeostatic threshold control (target rate 3%, gain 12). Engineering choices, not validated biology.
MaleCNS v1.0 flat connectome feathers (HHMI Janelia + Google, CC-BY) from
storage.googleapis.com/flyem-male-cns/v1.0/... — downloaded to
/library/datasets/malecns_v1/ (canonical dataset storage). The processed
CSR cache lives beside it in processed-flyc4/.
Training (k3s Job, pinned to k3s-w6 by user direction — normally this lane
selects hardware=rtx3090-x1 labels instead):
kubectl apply -f deploy/namespace.yaml
kubectl apply -f deploy/train-smoke.yaml # 3-batch pipeline check
kubectl logs -n flyc4 -f job/fly-smoke
kubectl apply -f deploy/train-job.yaml # full 200x768-game runServe after training:
kubectl apply -f deploy/web.yaml # NodePort 30180Two trained variants beyond tic-tac-toe (both: frozen connectome as feature extractor + tiny decoder, after jerryjliu/fly_ocr):
- Aerial fire fly — FLAME drone imagery, 3-class (fire/smoke/non-fire):
55.2% val accuracy (chance 33%). Report:
/library/datasets/flyc4/live/fire/report.json. - Satellite damage fly — Etkin satellite tiles, 5-class post-wildfire
damage: 60.8% val accuracy (chance 20%), 18,714 tiles through the frozen
circuit. Report:
/library/datasets/flyc4/live/fire_satellite/report.json. A pooled-pixel MLP baseline (no circuit) reaches 85.7% and is kept aspixel_baseline.npz— an earlier artifact under this name was that pixel MLP mislabeled as a circuit decoder.
Trainer: flyc4/fire_satellite.py (parquet shards → retina → circuit → decoder),
sample tiles for the web: flyc4/fire_samples.py.
The rigorous binary-classification version of this experiment — with rewired / random wiring controls, lesion ablations and CIs — now lives in PixelML/firefly (demo: NodePort 30181).
The web app has a fire watch mode (mode switch top-left, or ?mode=fire):
pick a dataset tile or upload one, the tile runs through the same frozen
circuit, and the page shows the 5-class verdict, the live connectome wave, and
the lesion switches degrading the fly in real time.