# fly-mania Un cerebro de mosca que aprende a jugar osu!mania 4K. El proyecto toma un subgrafo **fijo** del conectoma MaleCNS de *Drosophila* (3 418 neuronas y 242 111 sinapsis reales, que nunca se modifican) y le conecta un pequeño controlador entrenable. En cada instante la mosca solo recibe lo que hay en pantalla y el audio reciente. Decide, carril por carril, si pulsar, mantener o soltar. Sus patas llegan a las teclas a través de un actuador con retardo y variación, como un cuerpo real. ![Render de la mosca jugando](docs/figures/render_frame.jpg) *Izquierda: la mosca (NeuroMechFly) sobre su teclado de 4 patas, con la pantalla de osu! y el marcador en vivo. Derecha: la actividad de las 3 418 neuronas del conectoma y la salida del actuador hacia cada pata.* ## Cómo funciona ```text pantalla + audio ──► retina 4×128 + 8 bandas de audio (120 Hz) ──► conectoma MaleCNS fijo (3 418 neuronas) [congelado] ──► controlador: proyección + 4 GRU, una por carril [se entrena, 352 971 parámetros] ──► decisión por carril: REST · PRESS · RELEASE ──► actuador: 16 ms de retardo, ±14 ms de variación [física] ──► teclas D F J K, juzgadas con el puntaje oficial de osu!stable ``` - **Percepción causal:** la mosca ve una retina de notas (4 carriles × 128 filas) y 40 ms de audio pasado. No recibe la partitura, ni los tiempos, ni ayuda del maestro al jugar. La velocidad de scroll está normalizada. - **Entrenamiento por imitación (DAgger):** un *maestro* que conoce el mapa y el estado real del actuador indica, en cada fotograma, qué haría cada tecla. La mosca aprende a imitarlo desde las situaciones en que ella misma se mete. No hay recompensa. El maestro solo existe durante el entrenamiento. - **Un solo actuador** para entrenamiento, evaluación y render. La variación del tiempo sale de ese cuerpo, nunca de efectos añadidos al video. Los detalles están en [docs/](docs/README.md): conectoma y percepción, política y actuador, entrenamiento, evaluación y render. ## Instalación Funciona en **Windows** y **Linux** (el código es Python; macOS debería funcionar como Linux, pero no está probado). Necesitas Python 3.10+ (probado con 3.12). Para entrenar hace falta una GPU NVIDIA. No hay que instalar ffmpeg: lo trae `imageio-ffmpeg`. | Paso | Windows (doble clic o `cmd`) | Linux / macOS (terminal) | |---|---|---| | 1. Entorno | `setup_env.bat` | `./setup_env.sh` | | 2. Conectoma | `setup_connectome.bat` | `venv/bin/python scripts/setup_connectome.py` | | 3a. Renderizar con la interfaz | `render_ui.bat` | `venv/bin/python scripts/render_ui.py` | | 3b. Renderizar por consola | `render_video.bat ` | `venv/bin/python scripts/render_video.py ` | | 4. Entrenar | `train.bat ` | `venv/bin/python scripts/train.py ` | Los `.bat` son atajos a los mismos scripts de Python: aceptan exactamente las mismas opciones. En los ejemplos de este README, en Windows cambia `venv/bin/python scripts/X.py` por `X.bat`, o por `venv\Scripts\python.exe scripts\X.py`. ### 1. Entorno **Windows** - Instala Python desde [python.org](https://www.python.org/downloads/) y marca *Add python.exe to PATH*. - Ejecuta `setup_env.bat`. Crea `venv\` e instala PyTorch con CUDA 12.8 (en Windows, PyTorch de PyPI no trae CUDA) y las dependencias. Al final comprueba la GPU. - Sin GPU NVIDIA: `setup_env.bat --cpu`. - Otra versión de CUDA: `set TORCH_INDEX=https://download.pytorch.org/whl/cu126` y vuelve a ejecutarlo. **Linux / macOS** ```bash ./setup_env.sh # venv/, PyTorch (CUDA desde PyPI), dependencias y comprobación de la GPU ./setup_env.sh --cpu # sin GPU (renderiza, pero entrenar es muy lento) source venv/bin/activate ``` Para forzar una build concreta de PyTorch: `TORCH_INDEX=https://download.pytorch.org/whl/cu128 ./setup_env.sh`. ### 2. Conectoma (necesario para render y entrenamiento) ```bash venv/bin/python scripts/setup_connectome.py # Windows: setup_connectome.bat venv/bin/python scripts/setup_connectome.py --check # solo comprueba qué hay ``` El script descarga las tres tablas públicas de MaleCNS v1.0 (Janelia FlyEM, ~1.1 GB, reanudables y verificadas por MD5). Con ellas construye los dos archivos que usa el proyecto, en `data/connectome/male-cns-v1.0/`: | Archivo | Qué es | |---|---| | `sensoriomotor-v1.npz` | El subgrafo fijo: 3 418 neuronas visuales, auditivas, del cuerpo fungiforme, descendentes y motoras de T1, con sus 242 111 conexiones reales. | | `male_cns_hud_cache.npz` | Posiciones de las somas, para dibujar el panel del conectoma en el render. | Si los archivos ya existen, no se descarga ni se reconstruye nada. ### Modelo El repositorio incluye el checkpoint final, `runs/final/ckpt/e00072.pt` (~4 MB). Es el que usan por defecto el render y la evaluación, así que después de los pasos 1 y 2 ya puedes renderizar. Los demás checkpoints y los registros de entrenamiento no se versionan. ### Mapas Para renderizar sirve cualquier `.osu` de **osu!mania 4K**, siempre que esté en su propia carpeta junto a su audio y su imagen de fondo: ```text maps/ Camellia - Kikai No Tsubasa/ Camellia - Kikai No Tsubasa (Ycloki) [Celtic Gearheart].osu ← el .osu dice qué audio y qué fondo usar audio.mp3 ← (o .ogg, .wav…) bg.jpg ← (o .png, .jpeg…) ``` - El `.osu` nombra su audio (`AudioFilename:`) y su fondo (sección `[Events]`). Los tres archivos deben estar en la **misma subcarpeta**, como quedan al descomprimir un `.osz`. - **Sin audio**, el render funciona igual, pero el video sale mudo y la mosca "oye" silencio (sigue viendo las notas). - **Sin fondo**, la pantalla del juego sale con fondo liso. - Tanto el render como la interfaz avisan de lo que falta. - Lo más cómodo es descargar el `.osz` del mapa y subirlo desde la interfaz (lo descomprime en `maps/`), o copiar la carpeta a mano dentro de `maps/`. El corpus de entrenamiento está en `fly_osu_data/`. En git solo van sus `dataset.csv`; ver [fly_osu_data/README.md](fly_osu_data/README.md). ## Renderizar un mapa ### Con la interfaz ```bash venv/bin/python scripts/render_ui.py # Windows: render_ui.bat (abre el navegador solo) ``` Abre `http://localhost:8765`. Si la ejecutas en un servidor Linux sin pantalla, ábrela desde tu equipo con un túnel SSH (`ssh -L 8765:localhost:8765 `) o con el puerto abierto (`http://:8765`). En Windows solo escucha en tu propio equipo. **1. Elige el mapa y las opciones.** - **Mapa:** en el bloque superior, busca por título, artista, mapper o dificultad entre los mapas de `fly_osu_data/` y de `maps/`, o sube un `.osz`/`.osu`. La ficha de la derecha muestra el fondo, las estrellas, la duración, los objetos y las LN, el BPM y el OD, y avisa si faltan el audio o el fondo. - **Opciones:** checkpoint; mapa completo, un clip, o el mapa completo con el video solo desde un segundo dado (puntaje y combo se conservan); fps; velocidad de scroll; SV y PP en pantalla; cámara (fija, una o varias vistas estáticas, o una trayectoria grabada); nombre del archivo; procesos; y GPU o CPU. Debajo aparece siempre el comando equivalente de bash. ![Configuración del render](docs/figures/render_ui_setup.png) **2. Pulsa *Render video* y sigue el progreso.** La barra avanza por fases: simulación de la partida, preparación de la escena 3D, rasterizado en paralelo y codificación con el audio. Con varias vistas se simula una sola vez y se genera un video por vista. ![Render en curso](docs/figures/render_ui_progress.png) **3. Mira o descarga el resultado.** Los videos quedan en `renders/ui/`, junto a un informe JSON con puntaje, precisión y conteo de judgements. Se reproducen en la misma página. ![Render terminado](docs/figures/render_ui_done.png) ### Desde la consola `scripts/render_video.py` es el renderizador; la interfaz solo lo llama. En Windows usa `render_video.bat` con las mismas opciones (y `\` en las rutas). ```bash # Mapa completo, 60 fps venv/bin/python scripts/render_video.py --map-path ".osu" --full-map # Clip de 20 s desde el segundo 95 venv/bin/python scripts/render_video.py --map-path ".osu" --start-sec 95 --duration-sec 20 # Mapa completo, pero el video empieza en 6:00 (puntaje y combo desde el inicio) venv/bin/python scripts/render_video.py --map-path ".osu" --full-map --from-sec 360 # Varias cámaras estáticas con una sola simulación: un video por vista venv/bin/python scripts/render_video.py --map-path ".osu" --full-map \ --view original --view "lateral=-5.24,-4.49,1.9:1.04,-0.21,1.34" # Cámara en movimiento: grábala en el navegador y úsala en el render venv/bin/python scripts/camera_preview.py --map-path ".osu" --start-sec 60 --duration-sec 30 venv/bin/python scripts/render_video.py --map-path ".osu" --start-sec 60 --duration-sec 30 \ --camera-path renders/camera_preview//camera_path.json ``` | Opción | Uso | |---|---| | `--checkpoint` | Modelo (por defecto `runs/final/ckpt/e00072.pt`). | | `--map-path` | `.osu` absoluto, relativo al repo o a `fly_osu_data/{no_sv,sv}`. | | `--full-map` / `--start-sec` + `--duration-sec` | Mapa completo o un clip. | | `--from-sec` | Con `--full-map`: juega todo y graba desde ese segundo. | | `--fps` | Fotogramas del video (las decisiones siguen a 120 Hz). | | `--scroll-speed`, `--no-map-sv`, `--no-pp` | Solo afectan a lo que se ve, nunca a la partida. | | `--view NOMBRE=px,py,pz:tx,ty,tz` | Cámara estática (posición:objetivo), repetible; `original` es la fija. | | `--camera-path`, `--camera-smooth` | Trayectoria grabada con `camera_preview.py`. | | `--policy-seed` | Semilla de la variación del actuador. | | `--workers` | Procesos de rasterizado. Un mapa largo necesita ~1.5 GB de RAM por proceso. | | `--output-dir`, `--output-name` | Destino del `.mp4` y su informe `.json`. | ## Entrenar `scripts/train.py` entrena el controlador con DAgger. El conectoma queda congelado; se ajustan la proyección, los codificadores, las GRU y las cabezas de acción. **Cada época:** 1. Juega lotes de clips de 8 s del corpus a través del actuador real. 2. Aprende de las etiquetas del maestro en los estados que visitó. 3. Se valida con el puntaje oficial sobre un conjunto fijo de clips que no comparten audio con el entrenamiento. El nivel de intervención del maestro baja (1.0 → 0.75 → 0.5 → 0.25 → 0.1 → 0.0) solo tras tres validaciones seguidas que superan umbrales cada vez más estrictos. Requisitos: el conectoma, los mapas de `fly_osu_data/` y el manifiesto `data/training/training_manifest.csv`. El manifiesto se regenera con `scripts/data/` (ver [fly_osu_data/README.md](fly_osu_data/README.md)). En Windows usa `train.bat` con las mismas opciones. ```bash # Desde cero (incluye el gate de micro-fixtures), 64 actualizaciones por época, lotes de 8 venv/bin/python scripts/train.py --epochs 100 --output runs/run --checkpoints-dir runs/run/ckpt # Reanudar venv/bin/python scripts/train.py --resume-checkpoint runs/run/ckpt/e00043.pt --epochs 100 \ --output runs/run --checkpoints-dir runs/run/ckpt # Supervisión desatendida: reinicios acotados y ranking de checkpoints por validación venv/bin/python scripts/supervise_training.py --prefix train --target-epoch 100 # Curvas de validación venv/bin/python scripts/plot_training.py runs/run/metrics.json --out docs/figures/training_curves.png ``` Opciones útiles: - `--rollout-batch-size`: rollouts por tick de GPU. - `--workers`: procesos que preparan los clips. - `--benchmark-scale`: tamaño de la validación. - `--device`. El checkpoint se elige solo por validación, nunca con el test ni con mapas de prueba ([docs/04_entrenamiento.md](docs/04_entrenamiento.md)). ## Otros scripts | Script | Para qué | |---|---| | `scripts/evaluate_map.py` | Juega un mapa sin video y escribe el informe JSON (puntaje, judgements, errores de tiempo). | | `scripts/evaluate_test_split.py` | Evalúa el conjunto de test reservado. | | `scripts/camera_preview.py` | Visor 3D en el navegador para grabar trayectorias de cámara. | | `scripts/analyze_neural_activity.py` | Registra y resume la actividad del conectoma durante una partida. | | `scripts/setup_connectome.py` | Descarga y construye el conectoma (arriba). | | `scripts/data/` | Descarga del conectoma, inventario de mapas y construcción de manifiestos. | ```bash venv/bin/python scripts/evaluate_map.py --map-path ".osu" --full-map venv/bin/python -m unittest discover -s tests # pruebas ``` ## Estructura ```text scripts/ render_ui, render_video, train, evaluación, camera_preview, setup_connectome; data/ src/ environment/ (mapa, actuador, puntaje), training/, evaluation/, viewer/ (render e interfaces web) tests/ pruebas unitarias docs/ documentación y figuras data/ conectoma (descargado) y manifiestos de entrenamiento fly_osu_data/ corpus de mapas (solo dataset.csv en git) osu_ui/ skin usada por el render (Yugen PRO, de Garin) assets/ mallas NeuroMechFly de la mosca y fuentes DejaVu maps/ mapas propios o subidos desde la interfaz (fuera de git) runs/final/ckpt/e00072.pt checkpoint final (el resto de runs/ y renders/ quedan fuera de git) *.bat, setup_env.sh atajos para Windows y preparación del entorno ``` `gd_player/` es una línea independiente (Geometry Dash) con su propio código y documentación. ## Licencia y créditos El código y la documentación de fly-mania se publican bajo licencia [MIT](LICENSE). Los componentes de terceros conservan sus propios términos (detalle en [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)): - **Conectoma:** MaleCNS v1.0, de Janelia FlyEM ([male-cns.janelia.org](https://male-cns.janelia.org)). Se descarga; no se incluye en el repositorio. - **Modelo 3D de la mosca:** [NeuroMechFly / FlyGym](https://github.com/NeLy-EPFL/flygym) (NeLy, EPFL), bajo Apache-2.0. - **Skin de osu!mania:** *Yugen PRO*, de **Garin** ([perfil](https://osu.ppy.sh/users/2130664), [publicación original](https://osu.ppy.sh/community/forums/topics/365036)). Todos los derechos son de su autor; se retirará si lo pide. - **Ilustración de combo** (`osu_ui/Splash.png`): de **so bin** ([publicación](https://kamisama968.lofter.com/post/75cafbe5_2b7cdb0a8)). Todos los derechos son de su autor; se retirará si lo pide. - **Otros:** three.js (MIT) y las fuentes DejaVu. osu! es una marca de ppy Pty Ltd. Este proyecto no está afiliado a ppy.