Bitácora de experimentos: controlador neuroevolutivo contra el árbol de comportamiento de Lyra
Documento para la Dra. Ponce de León y el Dr. Cuéllar. Reúne en orden cronológico todo lo que se construyó, probó, corrigió y decidió desde el lunes 5 de octubre, con el motivo de cada cambio de diseño y las cifras que lo respaldan. Se indica de forma explícita lo que no se puede afirmar.
NEAT_Training/; las rutas se indican en la sección de archivos.Resumen
- Qué existe. Un controlador NEAT nativo dentro de Lyra (C++), con observación de 13 entradas y 5 salidas, evaluación por lotes sin ventana (8 instancias de Unreal en paralelo, tiempo acelerado ×3 en entrenamiento y ×1 en evaluación final) y un ciclo evolutivo con neat-python que intercambia genomas y resultados por archivos JSON.
- Qué se encontró. Con una semilla mínima de puntería, la evolución produjo en dos corridas independientes un controlador que sobrevive 30 a 38 s más que el BT de Lyra y que, según la línea y el escenario, empata o supera al BT en daño. En ningún caso la tasa de victoria es significativamente distinta de 50 %. Sin semilla, a 100 generaciones, una corrida alcanzó paridad en 3v3; a 60 generaciones no.
- Qué es ese controlador. Una red de 1 a 4 conexiones: gira hacia el enemigo, dispara siempre y casi no se mueve (velocidad media de 12 a 68 cm/s contra ≈400 del BT). Un controlador de tres reglas escrito a mano rinde igual o mejor. La evolución redescubrió esa táctica; no hay evidencia de que la supere. Su precisión de disparo es 4 a 7 % frente a 32 a 38 % del BT.
- Qué se validó del método. El fitness ordena correctamente controladores de referencia (aleatorio, inerte, reflejo a mano, BT); la medición es estable entre repeticiones; el entrenamiento a ×3 degrada algo la puntería; se identificaron y corrigieron dos causas técnicas por las que las primeras corridas no aprendían (deriva del giro vertical y mutaciones demasiado fuertes).
- Hallazgo de diseño de la madrugada del 9 de oct. El NEAT recibía la dirección y distancia al enemigo más cercano sin línea de visión, mientras que el BT apunta y dispara solo con lo que percibe. Se implementó una condición de percepción por vista, igual al BT en alcance (2500 uu) y ángulo (cono de 120°), y se relanzó la serie de experimentos. También se encontró que el BT navega hacia el enemigo más cercano del mapa cuando no percibe a nadie. Ambas asimetrías se declaran.
- Resultado bajo percepción por vista. Ningún controlador alcanza al BT en daño y bajas: seis campeones evaluados sin reentrenar y dos corridas entrenadas desde cero con esa restricción (una con semilla, 60 generaciones; una sin semilla, 100). La supervivencia mayor se conserva solo en los reflejos que casi no se mueven. Quitar la información privilegiada reduce el daño relativo en 70 a 140 puntos. No hay evidencia de un error de programación ni de corridas; hay evidencia de que la condición anterior inflaba los resultados y de que el entrenamiento por vista no basta con este diseño y presupuesto. No se puede concluir que NEAT no sirve.
- Etapa 2 bajo percepción por vista (E1). Entrenar 40 generaciones más contra 5 NEAT + 5 BT no mejoró al campeón: Δ de daño en 3v3 de −89.7 a −84.8 (intervalos solapados), tasa de victorias de 0.06 a 0.27 (Wilson 0.11–0.52), y peor en 5v5 y 10v2. No se cumple el criterio de «mejora clara» fijado antes (límite inferior del intervalo sobre −63.9). Con visión completa la misma etapa sí había dado una mejora de unos 25 puntos.
- Qué no se puede afirmar. Que NEAT supere al BT en general, que aprenda tácticas complejas, que sea reproducible más allá de n=2 por línea con semilla, ni que resista a jugadores humanos (no se ha medido).
Desarrollo técnico: cómo funciona NEAT y cómo se conecta con Unreal
Esta sección responde a las dudas sobre cómo se implementó NEAT en Python, cómo se configura la red, qué contiene un genoma y cómo se conecta con el controlador de Unreal.
Respuestas directas
- ¿Python y Unreal se comunican «en vivo» por un socket, con Learning Agents? No. La conexión final es por archivos JSON, generación por generación. Python escribe
genomas.json, lanza procesos de Unreal que juegan las partidas y leen ese archivo, y cada proceso escribe un archivo de resultados y termina. Python no está conectado durante la partida. La solicitud y conversaciones anteriores hablaban de un puente en tiempo real con Learning Agents; ese plugin se analizó a nivel de código fuente y no se usó, porque espera una política con capas fijas y NEAT cambia la estructura de cada individuo. - ¿La red aprende «en vivo», durante la partida? No. Durante un episodio los pesos no cambian. Lo que cambia entre generaciones es la población: NEAT selecciona, cruza y muta los genomas con base en el fitness medido.
- ¿La red corre en tiempo real dentro del juego? Sí. En Unreal, un cerebro escrito en C++ ejecuta la red 10 veces por segundo (cada 0.1 s de juego) sin Python. Se comprobó que calcula lo mismo que Python (ver «Verificación»).
- ¿Se entrena mientras una persona juega? No. Todo el entrenamiento ocurre en partidas de bots contra bots, en servidores sin ventana. Una persona podría jugar contra un campeón ya entrenado, pero eso todavía no se ha probado.
Arquitectura
flowchart LR A["evolve.py
neat-python: población de genomas"] -->|"genomas.json"| B["ue_evaluator.py
lanza N procesos de Unreal"] B --> C["UnrealEditor-Cmd
servidor sin ventana, L_GymAVE"] C --> D["LyraNeatBotController
cerebro C++ de cada genoma"] D --> E["LyraNeatEpisodeSubsystem
mide el episodio"] E -->|"launch_*.json"| B B -->|"resultados.json"| A A --> F["fitness.py
fitness por genoma"] F --> A
Ciclo de una generación, paso a paso
- Población.
evolve.pycrea la población conneat.Population(60 genomas) o la reanuda desde un checkpoint (--resume). - Escenarios. Para la generación g define K=4 escenarios con semillas
seed·100003 + g·1009 + k. Todos los genomas juegan los mismos escenarios, aunque las posiciones de aparición no se fijan. - Exportación.
ave_io.make_genomes_payloadescribegenomas.json: la interfaz (entradas, salidas, normalización), los identificadores de nodos de entrada y de salida, los escenarios y, por genoma, sus nodos y conexiones habilitadas. - Lanzamientos.
UnrealEvaluator.rundivide los genomas en bloques de 10 (o 5 en la etapa 2) y, para cada escenario y bloque, lanzaUnrealEditor-Cmd.execon-server -game -nullrhi, el mapaL_GymAVE?NumBots=…?Experience=B_AVE_Experience, un puerto propio y los argumentos-NeatGenomes,-NeatGenomeOffset,-NeatBotCount,-NeatGeneration,-NeatScenario,-NeatEpisodeSeconds,-NeatTimeDilationy-NeatResults. Con población 60, K=4 y bloques de 10 NEAT + 2 BT son 24 lanzamientos por generación, 8 en paralelo. - Dentro de Unreal. El componente de creación de bots (
LyraBotCreationComponent) asigna controlador NEAT a los primeros N bots y el Behavior Tree al resto. CadaALyraNeatBotControllercarga su genoma (índice = offset + ranura) y construye su red. El gestor de episodios espera la fasePlaying, acelera el tiempo ×3, mide la primera vida de cada bot NEAT durante 60 s (o hasta que mueran todos) y escribelaunch_*.json. Luego el proceso termina. - Unión. Python junta los resultados parciales en
resultados.jsony los valida (validate_results: versión, generación, genomas y campos). - Fitness.
fitness.genome_fitnesscalcula el fitness del episodio y promedia los K episodios de cada genoma. Se guardancomponents.csv(una fila por episodio) ycurve.csv(mejor, medio y número de especies por generación). - Reproducción. neat-python agrupa en especies, selecciona, cruza y muta, y deja un checkpoint por generación. Una generación toma unos 2.4 minutos en el entrenamiento normal.
Archivos de Python (carpeta NEAT_Training/)
| Archivo | Qué hace | Funciones o clases principales |
|---|---|---|
evolve.py | Orquestador del ciclo evolutivo. Semillas de población y reanudación | main, eval_genomes (que neat-python llama una vez por generación), banderas --seed-reflex, --resume, --per-launch, --bt-bots |
ue_evaluator.py | Lanza y coordina los procesos de Unreal | UnrealEvaluator: _command, _launch_on_port, run (pool de hilos, un puerto por instancia) |
ave_io.py | Formato de intercambio con Unreal | genome_to_dict, make_genomes_payload, validate_results, INTERFACE, EPISODE_FIELDS |
fitness.py | Función de aptitud | FitnessWeights, support_lambda, episode_components, episode_fitness, genome_fitness |
neat_config.txt | Configuración de neat-python | Secciones [NEAT], [DefaultGenome], [DefaultSpeciesSet], [DefaultStagnation], [DefaultReproduction] |
select_champion.py | Elige el campeón entre los 10 mejores con 8 escenarios nuevos | Reevaluación y reporte (champion_report.txt, champion.json) |
compare_bt.py | Compara un genoma contra el BT cara a cara, con la medición de disparos, cobertura y velocidad | Resumen en summary.txt, summary.json, matches.csv |
stats_compare.py | Estadística de las comparaciones (Wilson, binomial, bootstrap, Cliff d) | Escribe runs/estadistica_comparaciones.md |
calibrate_fitness.py, audit_measurement.py, ab_pitch.py, mutation_robustness.py | Pruebas de validación con protocolo escrito antes | Calibración con referencias, auditoría ×1 contra ×3, A/B del pitch, robustez a mutaciones |
json_net.py, make_golden.py | Referencia para comprobar la red de C++ | Evaluador sin neat-python; genera golden/golden_test.json |
mock_evaluator.py | Simulador falso para probar el ciclo sin Unreal | Se usó el 5 de octubre (demo_mock) |
dataset.py, train.py | Carga de la telemetría humana y primer entrenamiento offline (MSE) | Línea descartada como principal |
overnight.py, night2.py a night5.py, phase2.py, stage2.py, night_check.py | Orquestadores reanudables y chequeo de corridas | Colas de experimentos con estado en runs/overnight_state.json |
make_figures.py | Figuras para el reporte y el cartel (PIL) | Curvas, red del campeón, barras de comparación |
Configuración de la red (neat-python)
| Parámetro | Valor |
|---|---|
| Entradas y salidas | 13 y 5; 0 nodos ocultos al inicio; conexión inicial completa directa (65 conexiones) |
| Tipo de red | Hacia adelante (feed_forward = True); sin memoria |
| Activación y agregación | tanh (en neat-python equivale a tanh(2.5·z)) y suma; sin mutación de activación |
| Pesos y sesgos | Inicio con media 0 y desviación 1; mutación suave: pesos 30 % con potencia 0.1, sesgos 20 % con potencia 0.1, reemplazo 2 %; rango ±30 |
| Estructura | Agregar conexión 0.10; quitar conexión 0.05; agregar nodo 0.05; quitar nodo 0.02; habilitar o deshabilitar 0.05 |
| Especiación | Umbral de compatibilidad 1.5; coeficientes 1.0 (genes disjuntos) y 0.5 (pesos) |
| Estancamiento y reproducción | Máximo 20 generaciones sin mejora; élite 2 por especie y 2 globales; sobreviven el 20 % mejor |
| Población | 60 por corrida (el archivo dice 100; se reemplaza con --pop-size 60) |
El genoma
En neat-python un genoma es un conjunto de genes de nodo (sesgo, respuesta, activación, agregación) y de conexión (origen, destino, peso, habilitada). Las entradas llevan identificadores −1 a −13 y las salidas 0 a 4. genome_to_dict exporta solo las conexiones habilitadas. Ejemplo real, el reflejo con retroceso hecho a mano (id 9002), con la estructura del campeón 7837: cinco nodos de salida con sesgos (avance −0.21, disparo +2.65) y una sola conexión, de la entrada −9 (ángulo hacia el enemigo) a la salida 2 (giro), con peso 4.94.
| Entrada | Qué es | Normalización |
|---|---|---|
| health | Vida propia | 0 a 1 |
| dir_x, dir_y, dir_z | Vector unitario hacia el enemigo (ojos a ojos) | −1 a 1; 0 si no hay enemigo |
| distance | Distancia al enemigo | Entre el radio de escaneo (5000 uu, 2500 en percepción por vista); 1 si no hay enemigo |
| has_enemy, vision | Hay enemigo; línea de visión despejada | 0 o 1 |
| aim_error | Error de apuntado | Grados entre 180; −1 si no hay enemigo |
| bearing | Giro necesario hacia el enemigo, con signo | Grados entre 180, recortado a [−1, 1] |
| ammo_frac | Munición del cargador | 0 a 1; −1 si se desconoce |
| ray_front, ray_left, ray_right | Tres rayos de obstáculos (0°, −45°, +45°, 500 uu) | 0 a 1; 1 = libre |
| Salida | Uso en el juego |
|---|---|
| in_forward, in_right | Entrada de movimiento adelante/atrás y lateral, de −1 a 1 (AddMovementInput) |
| in_yaw | Velocidad de giro horizontal: salida × 180°/s |
| in_pitch | Velocidad de giro vertical: salida × 60°/s (anulada en los experimentos, 0°/s) |
| in_shoot | Dispara si la salida es mayor que 0 |
Archivos de C++ (Source/LyraGame/Neat/ y relacionados)
| Archivo | Qué hace |
|---|---|
LyraNeatBrain | Lee genomas.json (LoadGenomesFile, LoadFromGenomeJson), ordena los nodos de forma topológica y evalúa la red (Evaluate): por nodo, tanh(2.5·(sesgo + respuesta·suma de pesos·fuentes)). Sin Python en tiempo de ejecución. Incluye el comando ave.Neat.Golden para la prueba de equivalencia. |
LyraNeatObservation | Construye las 13 entradas (BuildObservation, ToNetworkInputs). La telemetría humana usa el mismo código, de modo que humano y bot se registran con las mismas variables. |
LyraNeatBotController | Derivado del controlador de bots de Lyra. Cada 0.1 s ejecuta Think: construye entradas, evalúa la red y guarda las salidas. En cada Tick aplica movimiento (AddMovementInput), giro limitado y disparo mediante las etiquetas de entrada de habilidades. Acumula las estadísticas del episodio. |
LyraNeatEpisodeSubsystem | Existe solo con -NeatResults. Espera la fase Playing, fija la dilatación de tiempo, mide el episodio, escribe el JSON de resultados y cierra el proceso. Con -NeatMeasureBt mide también a los bots del Behavior Tree (daño, disparos, cobertura y velocidad). |
LyraBotCreationComponent | Los primeros -NeatBotCount bots que crea el modo de juego usan el controlador NEAT; el resto conserva el Behavior Tree. |
LyraTelemetryComponent | Registra las sesiones humanas a 10 Hz (CSV) con la misma observación; desde el 9 de octubre añade la columna speed. |
Las banderas de percepción por vista (-NeatRequireLos, -NeatFovHalfDeg, -NeatSightRadius) y las de experimento (-NeatMaxPitchDegPerSecond, -NeatGenomeId, -NeatMeasureBt) se pasan por línea de comandos, y la variable de entorno NEAT_EXTRA_ARGS las agrega a todos los lanzamientos de Python.
Función de fitness, tal como está implementada
Por episodio: F = 1.0·Dd + 0.2·Ts − 0.2·Dr + λ(g)·(0.05·Fcerca + 4.0·Fapunta), con Dd = min(daño hecho / 100, 3), Ts = min(tiempo vivo / 60 s, 1), Dr = min(daño recibido / 100, 1), λ(g) = max(0, 1 − g/30), Fcerca la fracción de ticks con el enemigo a distancia media y Fapunta la alineación media de la mira (1 − error/90°, mínimo 0). El fitness del genoma es el promedio de sus 4 episodios. El peso de Fvis es 0.
Verificación de que Unreal calcula lo mismo que Python
El 9 de octubre, el comando ave.Neat.Golden comparó la red de C++ con la referencia de Python sobre golden/golden_test.json: 140 vectores de entrada, error máximo 1.4·10⁻¹⁵, con tolerancia de 10⁻⁵. Resultado: aprobado. Además, mock_evaluator.py permitió probar el ciclo completo sin Unreal el 5 de octubre.
Limitaciones técnicas del diseño
- Red hacia adelante, sin memoria: no recuerda dónde vio al enemigo.
- El entrenamiento reinicia un proceso de Unreal por partida; es estable, pero cuesta tiempo de arranque.
- Las posiciones de aparición no se fijan entre genomas; es una fuente de ruido.
- El giro vertical se anuló por la deriva observada; la mira del NEAT solo gira en horizontal.
- La aceleración ×3 del tiempo reduce un poco la puntería (0.96 a 0.88 en una prueba), por eso la evaluación final se hace a ×1.
Línea de tiempo
Primero, el ritmo de trabajo medido. Cada celda es una hora; está activa si ese día se escribió al menos un archivo de resultados en NEAT_Training/runs/. Es una medida mínima de que la computadora estuvo ejecutando experimentos: no cuenta las pruebas que no dejan archivos ni el trabajo de redacción.
53 horas con actividad de 98 transcurridas desde el 5 de oct 16:00 (corte: 9 de oct, 17:37). El 8 de oct hubo actividad en 21 de 24 horas y el 9 de oct en 16 de las 18 horas transcurridas. Pausas largas: 7 de oct de 05:00 a 18:00; 8 de oct de 08:00 a 11:00; y 9 de oct de unos 09:20 a 12:05, entre el fin de la cola de la noche y el siguiente experimento, mientras se analizaban los resultados y se escribía el protocolo (sin corridas en ese lapso). El 9 de oct a las 00:45 se detuvo todo por decisión del responsable para corregir el diseño, y se reanudó a las 01:55. Desde las 12:50 corre la cola de reentrenamiento y sigue en marcha. Las horas en recuadro punteado todavía no ocurren.
Ajuste por error cuadrático medio (MSE) sobre los datos de las sesiones humanas. Dio salidas casi constantes (MSE 0.749 y 0.739); no se usa como línea principal. Los humanos registrados no son el objetivo de imitación: la solicitud pide evolucionar, no imitar.
Un solo fitness de combate, referencia humana como dato de contraste y no como entrenamiento, integración por lotes y dos carteles distintos (congreso y miniproyecto).
Módulos ave_io.py y dataset.py, y una corrida de 40 generaciones contra un simulador falso (demo_mock) para probar el ciclo evolutivo antes de usar el motor.
4 generaciones, 800 episodios, evaluación por lotes con servidor dedicado sin ventana.
El mapa tiene una fase Warmup antes de Playing. Las corridas piloto, 2 y 3 se descartaron; el gestor de episodios ahora espera la fase Playing.
Corrida 5 (K=2): la correlación entre dos episodios del mismo genoma era 0.09. Más del 80 % de lo que veía la selección era azar. La puntería es el único término estable (≈0.44). Cambio: K=4, población 60, peso de la ayuda de puntería 1.0 → 4.0.
Observación del Dr. Cuéllar: ver al enemigo no implica mala conducta. Los datos coincidieron: efecto mínimo y sin repetibilidad entre episodios del mismo genoma.
A los 22:20 se disparó una regla fijada de antemano (la puntería no subía) y se subió el peso de puntería a 8. La corrida no aprendió a combatir.
Se había afirmado a partir de una partida de prueba de 2 bots. Una validación de 10 partidas mostró que un genoma mínimo hecho a mano supera al BT cara a cara (daño 112.5 contra 76.5, bajas 0.73 contra 0.43). El fitness sí ordena bien. Lo que fallaba era la búsqueda.
Semilla mínima de puntería (7), umbral de especiación 3.0 → 1.5 porque la corrida 6 tuvo siempre una sola especie (8), semilla de reflejo (9, 10), pesos 0.2/0.2 (11). La 9 se descartó por un error: los sesgos aleatorios de giro saturaban la salida.
A las 21:35 se concluyó que con los pesos 0.5/0.5 pelear puntuaba menos que esconderse, con base en 20 episodios. La calibración formal (40 episodios por referencia, rejilla de 25 combinaciones de pesos) lo desmintió: el reflejo supera al inerte y al aleatorio en todas las celdas. El cambio a 0.2/0.2 no era necesario; se conservó y se declara. El diseño del fitness se congeló.
La corrida 11 quedó en la generación 28.
Protocolos escritos antes de ver datos. Resultado: dos causas independientes y ambas necesarias explican que las corridas no aprendieran: (1) el giro vertical es una velocidad acumulada sin realimentación y deriva; (2) las mutaciones por defecto destruyen controladores competentes. Con el giro vertical anulado y mutación suave, los descendientes competentes pasan de 1–4 de 10 a 5–6 de 10.
Mutación suave, pitch anulado, resto igual. Aprobada por el responsable.
Sin colapso al apagar la ayuda (g30). Campeón 5582, elegido entre los 10 mejores con 8 escenarios nuevos.
Aprende (la puntería sube de 0.28 a ≈0.6), a diferencia de las corridas 6, 8 y 10, pero a 60 generaciones queda muy por debajo del BT (daño 49 contra 173 en 3v3).
Con 60 generaciones, la línea con semilla empata al BT en daño y bajas; la que no tiene semilla pierde 17 de 20 partidas.
Criterio fijado antes: daño y bajas ≥ BT en 3v3 y 5v5, y no peor que el campeón de la generación 100 en 10v2. Cumplido. Campeón 7837.
Daño 88.5 contra 99.5 del BT. A 100 generaciones la semilla ya no es necesaria para llegar a la paridad; acelera el aprendizaje.
Nueva instrumentación en C++ para medir precisión de disparo (impactos entre disparos) y cobertura, pedidas en la solicitud.
Una conexión habilitada (rumbo al enemigo → giro) más sesgos: dispara siempre y tiene un sesgo de avance negativo. Se interpretó como retroceder despacio; la velocidad medida después (≈20 cm/s) indica que casi no se mueve: «girar hacia el enemigo y disparar desde una posición fija».
Con intervalos de confianza, «supera ligeramente en daño y bajas» no se sostiene para la corrida 12. Lo correcto: no se distingue del BT y sobrevive claramente más.
Mismo patrón: sobrevive 35 a 38 s más; en 3v3 y 5v5 supera al BT en daño con intervalo que excluye 0. La etapa 2 se replica.
Reflejo con retroceso (id 9002), 3v3: daño 101.8 contra 71.8 del BT; bajas 0.82 contra 0.33; vive 60 contra 15 s. La evolución redescubre la táctica; no hay evidencia de que la mejore.
Verificado en el código de la observación: dirección, distancia, ángulo y error de apuntado apuntan al enemigo vivo más cercano a 5000 uu, sin exigir línea de visión y en 360°. Amenaza a la validez de la comparación. El responsable ordenó pausar todo (00:45).
El BT percibe a ≤2500 uu en un cono de 120° y sin memoria (bIncludeKnownActors=False). Su mira se ajusta de forma instantánea al objetivo percibido (verificado en el código del motor). Cuando no percibe a nadie, navega hacia el enemigo vivo más cercano de todo el mapa (EQS_GetAllEnemy, sin filtro de visión).
Compilación en 13 s. Prueba de humo con el reflejo con retroceso: con la observación original, 205 de 498 muestras (41 %) tenían enemigo sin línea de visión; con el filtro, 0 de 495.
FAIR_PROTOCOL.md fija las condiciones, los pasos y qué se reporta antes de ver resultados. Pasos: medir los campeones actuales con el filtro, entrenar con semilla (60 generaciones) y entrenar sin semilla (100 generaciones). Estimado de término: alrededor de las 11:30.
Sin reentrenar. El daño relativo de todos baja entre 70 y 140 puntos; la supervivencia se mantiene en los que retroceden.
Sin fallos. Campeón 3332: pierde 16 de 20 partidas 3v3 contra el BT.
La población no aprende: la compuerta de fitness cae a 0.03–0.08. Campeón 5598: pierde 16 de 20 en 3v3 y todas las de 5v5 y 10v2.
Lectura de los resultados, diagnóstico de hipótesis, plan de reentrenamiento y protocolo escrito antes de ver datos. En ese lapso no corrieron experimentos.
El bot NEAT casi no se mueve en los reflejos y campeones de etapa 2 (≈12 a 68 cm/s); el BT se mueve a ≈400 cm/s. Prueba de humo con 2 partidas y medición con 10 partidas por controlador.
Sin fallos. El campeón (5593) pierde 11 de 20 partidas 3v3, con Δ de daño −84.8. No mejora de forma clara respecto a la línea anterior (−89.7).
En marcha: 32 de 60 generaciones a las 18:01, sin fallos. Sigue su etapa 2 (E2b) y después los entrenamientos contra el BT desde la generación 0 (E3 a E5). Estimado: E2 termina hacia las 19:15; E2b hacia las 23:10; E3 de las 23:10 a las 07:00 del sábado.
Aprobada: 140 vectores de entrada, error máximo de 1.4·10⁻¹⁵ con tolerancia de 10⁻⁵.
El reflejo con retroceso y giro de búsqueda (9003) pierde 17 de 20 partidas y hace menos daño que sin búsqueda. La predicción se había anotado antes de correrlo.
Cómo leer la serie: desarrollo y condiciones
Los experimentos se pueden leer en dos etapas. La serie no se planeó como un diseño factorial desde el primer día; surgió de ir descubriendo qué variables importaban. Cada condición nueva se anotó con su motivo en el momento en que apareció, y eso es lo que permite compararlas ahora.
- Desarrollo (28 sep a 7 oct, 22:00). Construir el sistema y depurarlo: corridas piloto a 11. Aquí se encontraron y corrigieron errores del propio montaje. No se comparan entre sí.
- Condiciones experimentales (desde el 8 oct, 01:00). Con el fitness congelado desde el 7 de oct (1.0·Dd + 0.2·Ts − 0.2·Dr + ayuda de puntería que se apaga en la generación 30), K=4, población 60, configuración NEAT v2 y los mismos escenarios de evaluación. Lo que cambia entre condiciones es la información del enemigo, la inicialización y el oponente de entrenamiento.
| Condición | Información del enemigo | Inicialización | Entrena contra | Corridas | Estado |
|---|---|---|---|---|---|
| C1 | Completa: ≤5000 uu, 360°, sin línea de visión | Semilla de reflejo | 10 NEAT + 2 BT | run12 (100), rep1 (60) | Hecha |
| C2 | Completa | Población mínima aleatoria | 10 NEAT + 2 BT | control_sinsemilla (100) | Hecha, n=1 |
| C3 | Completa | Continúa C1 | 5 NEAT + 5 BT (etapa 2) | run13_etapa2, rep1_etapa2 (40 c/u) | Hecha |
| C4 | Por vista: línea de visión, ±60°, ≤2500 uu | Semilla de reflejo | 10 NEAT + 2 BT | fair_seed (60) | Hecha |
| C5 | Por vista | Población mínima aleatoria | 10 NEAT + 2 BT | fair_sinsemilla (100) | Hecha |
| M | Por vista, sin reentrenar | Campeones de C1 a C3 | — | fair_medicion | Hecha |
| Ref. | Completa y por vista | Controladores a mano 9001 y 9002 | — | comparaciones directas | Hecha en ambas |
En C4 y C5 cambia el significado de dos términos del fitness: Fapunta y Fcerca solo cuentan con enemigo percibido. El resto de la fórmula es idéntico.
Ventajas y desventajas del NEAT frente al BT, por condición
| Aspecto | NEAT, información completa (C1–C3) | NEAT, por vista (C4, C5) | BT de Lyra |
|---|---|---|---|
| Ve al enemigo a través de paredes para apuntar | Sí ventaja | No | No |
| Ángulo de visión | 360° (ventaja) | 120° | 120° |
| Alcance | 5000 uu | 2500 uu | 2500 uu |
| Conoce dónde está el enemigo para buscarlo | Sí, hasta 5000 uu | No desventaja | Sí todo el mapa |
| Mira | ≤180°/s, sin giro vertical (desventaja) | Igual | Instantánea, con giro vertical |
| Navegación, recarga, búsqueda de armas | No (desventaja) | No | Sí |
| Frecuencia de decisión | 10 Hz | 10 Hz | Objetivo cada ≈0.5 s |
| Memoria del enemigo | No | No | Solo percibidos en ese momento |
Con información completa, el NEAT tenía ventajas de información y desventajas de ejecución. Con percepción por vista pierde la ventaja de información y conserva las desventajas de ejecución, más la de búsqueda. Comparar C1 con C4 mide cuánto valía esa información. Es un resultado, no solo una corrección.
Qué significa para los jugadores humanos
- Lo medido con humanos. Solo hay 33 sesiones de humanos contra el BT (el BT ganó 28). No hay sesiones de humanos contra el NEAT. Los resultados del NEAT contra el BT no se pueden trasladar a humanos sin medirlo.
- La condición de información completa no sería aceptable como oponente. Un jugador percibiría como trampa que el NPC apunte a través de paredes. La condición por vista se parece más a lo que tiene un jugador. El BT también tiene ventaja de información al buscar (conoce posiciones), algo habitual en juegos; «justo» depende del marco que se elija.
- Riesgo de ajuste al BT. El campeón que más sobrevive aprendió a disparar casi sin moverse contra un BT que ataca de frente. No sabemos si esa táctica funciona contra una persona. Es una hipótesis que se puede comprobar con unas pocas partidas jugadas.
- Un NPC que no sabe buscar. Con percepción por vista, el NEAT solo reacciona a lo que ve. Como oponente de un humano necesitaría una forma de desplazarse; eso queda como trabajo futuro y no se afirma aquí.
Inventario de corridas
Se conservan todas, también las descartadas, porque cada una explica una decisión.
| Corrida | Gen. | Qué probaba | Estado |
|---|---|---|---|
piloto, run2, run3 | 4 · 2 · 15 | Primeras corridas en Unreal | Descartadas incluían el calentamiento de 30 s |
run5 | 7 | Fiabilidad de la señal (K=2) | Conservada motivó K=4, población 60 |
run6 | 122 | Sin semilla, pitch activo, mutación por defecto | Sin aprendizaje causa técnica identificada después |
run7, run8 | 3 · 28 | Semilla mínima; y especiación 1.5 | Detenidas |
run9, run10, run11 | 3 · 19 · 28 | Semilla de reflejo; pesos 0.2/0.2 | Descartadas / detenidas error de sesgos (9); sustituidas por la configuración v2 tras el A/B de pitch (10, 11) |
run12 | 100 | Configuración v2, con semilla de reflejo | Válida |
control_sinsemilla | 100 | Misma configuración, población mínima aleatoria | Válida (g60 archivada) |
run13_etapa2 | 40 | Continuación de run12, 5 NEAT + 5 BT | Válida |
rep1, rep1_etapa2 | 60 · 40 | Réplica con otra semilla aleatoria y su etapa 2 | Válidas |
control_replica | 20 de 100 | Réplica del control sin semilla | Pausada diseño con visión completa |
fair_seed, fair_sinsemilla | 60 · 100 | Percepción por vista, con y sin semilla | Válidas terminaron el 9 de oct |
demo_mock | 40 | Simulador falso para probar el ciclo | Solo pruebas |
Experimentos de validación del método
Estas pruebas no buscan un mejor controlador, sino saber si las medidas del proyecto se pueden creer.
Calibración del fitness con controladores de referencia
- Pregunta
- ¿El fitness ordena a un buen combatiente por encima de uno que no hace nada o actúa al azar?
- Qué se hizo
- 40 episodios por referencia en el entorno de entrenamiento y 24 del BT. Rejilla de pesos 0 a 0.5 para tiempo vivo y daño recibido.
- Resultado
- Aleatoria 0.145, inerte 0.15, reflejo a mano 0.81, BT 1.54. El reflejo supera al inerte y al aleatorio por más de 2 errores estándar en las 25 celdas de pesos.
- Decisión
- Fitness congelado: Dd + 0.2·Ts − 0.2·Dr + λ(g)·(0.05·Fcerca + 4.0·Fapunta).
Auditoría de la medición
- Pregunta
- ¿Las partidas rápidas (×3) y la bandera de medición del BT alteran los resultados?
- Qué se hizo
- 40 episodios por condición con el mismo controlador, incluida una réplica idéntica para medir el ruido de base.
- Resultado
- Daño, bajas y tiempo vivo sin diferencia significativa entre ×1 y ×3. La puntería baja de 0.96 a 0.88 a ×3 (z=4.9). Entre repeticiones idénticas el tiempo vivo varía entre 15 y 22 s.
- Decisión
- Entrenar a ×3 y evaluar a ×1; no interpretar diferencias menores que el ruido de base.
Giro vertical y mutaciones (A/B pre-registrado)
- Pregunta
- ¿Por qué los controladores competentes dejaban de serlo al mutar?
- Qué se hizo
- Mismos genomas en tres brazos (actual, pitch anulado, pitch recentrado), 10 genomas por conjunto, K=4. Reglas de decisión escritas antes.
- Resultado
- Reflejo con tres mutaciones suaves: competentes 0 de 10 con pitch actual contra 6 de 10 con pitch anulado. Población aleatoria: puntería 0.054 → 0.249. Con pitch anulado, mutación suave conserva 5–6 de 10; la estándar, 1–4 de 10. Reflejo sin mutar: 8–9 de 10 en todos los brazos.
- Decisión
- Pitch anulado y mutación suave. El mecanismo de la deriva es inferido, no medido directamente.
Fiabilidad y especiación
- Fiabilidad
- Correlación entre episodios del mismo genoma: 0.09 (corrida 5, K=2) → 0.53 a 0.72 por episodio en la corrida 12 (≈0.9 con K=4). Depende de cuán distinta es la población, no solo del método de medición.
- Especiación
- Con umbral 3.0 hubo una sola especie en 122 generaciones. Con 1.5, la población final de la corrida 12 tuvo 2 especies; con 0.4, habría 5, y con 0.3, 10. La variante 0.35 estaba en la cola y sigue pendiente.
Resultados contra el BT
Partidas nuevas, a velocidad ×1, solo la primera vida de cada bot, media por bot. «Gana» es el equipo con más bajas por bot en la partida. Intervalo de Wilson al 95 %; diferencias por partida con remuestreo (bootstrap).
3 contra 3, 90 s, 20 partidas
| Controlador | Daño NEAT / BT | Bajas NEAT / BT | Vivo (s) NEAT / BT |
|---|---|---|---|
| run12 g60 (3330), con semilla | 91 / 108 | 0.70 / 0.65 | 27 / 18 |
| run12 g100 (5582) | 83 / 93 | 0.65 / 0.47 | 41 / 17 |
| Control sin semilla g60 (3335) | 49 / 173 | 0.23 / 1.28 | 16 / 21 |
| Control sin semilla g100 (5587) | 89 / 100 | 0.72 / 0.48 | 39 / 18 |
| Etapa 2 de run12 (7837) | 101 / 87 | 0.68 / 0.50 | 47 / 17 |
| Réplica 1 g60 (3451) | 100 / 83 | 0.55 / 0.47 | 51 / 15 |
| Etapa 2 de réplica 1 (5768) | 101 / 79 | 0.62 / 0.38 | 53 / 15 |
| Reflejo a mano (9001) | 100 / 107 | 0.63 / 0.72 | 10 / 15 |
| Reflejo con retroceso a mano (9002) | 102 / 72 | 0.82 / 0.33 | 60 / 15 |
Estadística de los campeones principales
| Comparación | V/E/D | Tasa (Wilson) | Δ daño (IC 95 %) | Δ bajas (IC 95 %) | Δ vivo, s (IC 95 %) |
|---|---|---|---|---|---|
| Etapa 2 run12, 3v3 | 10/4/6 | 0.62 (0.39–0.82) | +14.7 (−2.9, +34.0) | +0.18 (−0.18, +0.55) | +30.1 (+22.7, +38.0) |
| Etapa 2 run12, 5v5 | 7/1/4 | 0.64 (0.35–0.85) | +6.6 (−28.0, +36.5) | +0.27 (−0.10, +0.62) | +32.0 (+26.0, +38.4) |
| Etapa 2 réplica 1, 3v3 | 10/7/3 | 0.77 (0.50–0.92) | +21.9 (+3.0, +40.8) | +0.23 (−0.00, +0.47) | +37.5 (+28.9, +46.0) |
| Etapa 2 réplica 1, 5v5 | 8/2/2 | 0.80 (0.49–0.94) | +30.9 (+15.4, +45.5) | +0.38 (+0.15, +0.63) | +35.5 (+31.3, +39.7) |
| Control sin semilla g100, 3v3 | 13/2/5 | 0.72 (0.49–0.88) | −10.9 (−38.0, +14.6) | +0.23 (−0.08, +0.53) | +20.9 (+12.4, +29.5) |
| Control sin semilla g60, 3v3 | 1/2/17 | 0.06 (0.01–0.26) | −123.3 (−158.1, −90.3) | −1.05 (−1.35, −0.75) | −4.5 (−8.4, −0.3) |
Un intervalo que no contiene 0 es evidencia de diferencia entre el NEAT y el BT en esa métrica. Con 12 a 20 partidas por comparación y sin corrección por comparaciones múltiples, ninguna tasa de victoria es significativamente distinta de 50 %. Los intervalos del reflejo con retroceso (9002) están pendientes de calcular.
Lo que muestran las métricas extra
- Precisión de disparo (impactos entre disparos): NEAT 4 a 7 %, BT 32 a 38 %. El NEAT dispara siempre; su ventaja viene de sobrevivir, no de apuntar mejor.
- Cobertura (enemigo a menos de 1500 uu, cubierto = sin línea de visión). Definición operacional pendiente de validar por el Dr. Cuéllar. Los campeones de etapa 2 pasan menos tiempo a cubierto que el BT (0.24–0.29 contra 0.34–0.37 de la fracción), pero reciben menos daño por segundo expuesto (14 a 19 contra 46 a 55). La lectura es que no usan cobertura: se quedan casi quietos mientras disparan (ver «Velocidad medida»). El tiempo con enemigo cerca es de 2 a 3 s por bot, así que es una tendencia.
- Estructura de los campeones. 7837: una conexión (rumbo → giro, +4.94) y sesgos de disparo (+2.65) y avance (−0.21: la entrada de movimiento sería −0.48, pero la velocidad medida es ≈20 cm/s). 5768: 4 conexiones, la mayoría de efecto pequeño. El promedio de la población en la etapa 2 no mejoró; el campeón es un valor atípico, validado en partidas nuevas.
El BT de Lyra, tal como se verificó en los activos
Tres ramas bajo un selector: sin munición, busca arma (EQS_FindWeapon); con munición y objetivo percibido, «Shoot And Move» (fija el foco en el objetivo, dispara cada ≈0.1 s y se mueve a una posición de EQS_MoveAgainstEnnemy con strafe); con munición y sin objetivo, recarga, busca arma y busca al jugador. El objetivo lo escribe un servicio que corre EQS_AIPerceptionEnemy. Las decisiones son ramas discretas, pero las acciones son continuas. Referencia humana: 33 sesiones (106.5 min, 247 vidas), el BT ganó 28; bajas y muertes por sesión 2.9 y 6.8.
Resultados bajo percepción por vista
Mismos escenarios y mismo protocolo de evaluación que antes (a ×1, partidas nuevas, 3v3 de 90 s con 20 partidas). Solo cambia lo que recibe el NEAT: el enemigo existe únicamente si hay línea de visión, está dentro de ±60° y a ≤2500 uu. Δ = NEAT menos BT, en promedio por bot.
Los mismos campeones, con visión completa y por vista
| Controlador | Δ daño, visión completa | Δ daño, por vista (IC 95 %) | V/E/D por vista | Δ vivo (s): completa → vista |
|---|---|---|---|---|
| Etapa 2 de run12 (7837) | +14.7 | −92.5 (−121.3, −66.2) | 2/4/14 | +30.1 → +27.6 |
| Etapa 2 de réplica 1 (5768) | +21.9 | −83.0 (−109.0, −58.6) | 1/4/15 | +37.5 → +26.4 |
| run12 g100 (5582) | −9.9 | −81.9 (−115.7, −48.0) | 3/6/11 | +24.1 → +27.9 |
| Control sin semilla g100 (5587) | −10.9 | −152.6 (−175.2, −130.5) | 1/1/18 | +20.9 → +12.8 |
| Reflejo a mano (9001) | −6.5 | −126.9 (−160.2, −93.5) | 0/3/17 | −4.6 → +1.0 |
| Reflejo con retroceso a mano (9002) | +30.0 (+16.5, +43.5); 13/5/2 | −98.2 (−129.3, −66.8) | 1/3/16 | +45.4 → +25.5 |
Con visión completa, el reflejo con retroceso hecho a mano ganó 13 de 20 partidas al BT (tasa 0.87, Wilson 0.62–0.96), el único resultado de toda la serie con significancia sobre 50 %. Por vista pierde 16 de 20. Para los seis controladores, quitar la información privilegiada reduce el daño relativo en 70 a 140 puntos. El tiempo vivo se mantiene en los que casi no se mueven (+25 a +28 s).
Controladores entrenados bajo percepción por vista
| Corrida | Escenario | V/E/D | Δ daño (IC 95 %) | Δ bajas (IC 95 %) | Δ vivo, s (IC 95 %) |
|---|---|---|---|---|---|
| Con semilla, 60 gen (3332) | 3v3 | 1/3/16 | −89.7 (−116.5, −63.9) | −0.68 (−0.97, −0.40) | −2.3 (−6.3, +1.9) |
| 5v5 | 3/1/8 | −49.2 (−82.4, −17.0) | −0.37 (−0.70, −0.07) | +0.7 (−3.2, +5.1) | |
| 10v2 | 6/1/5 | −36.4 (−66.3, −2.4) | −0.06 (−0.35, +0.21) | +12.4 (+7.4, +17.0) | |
| Etapa 2 de la anterior, 40 gen (5593) | 3v3 | 4/5/11 | −84.8 (−116.5, −53.7) | −0.40 (−0.72, −0.08) | +7.7 (+1.6, +14.3) |
| 5v5 | 3/2/7 | −96.5 (−138.8, −54.6) | −0.37 (−0.83, +0.05) | −2.5 (−6.7, +1.9) | |
| 10v2 | 2/1/9 | −109.9 (−173.3, −53.6) | −0.52 (−0.94, −0.10) | +5.4 (−2.7, +13.2) | |
| Sin semilla, 100 gen (5598) | 3v3 | 0/4/16 | −199.8 (−246.9, −150.6) | −1.37 (−1.75, −0.98) | −12.2 (−22.0, −4.4) |
| 5v5 | 0/0/12 | −188.7 (−221.1, −151.2) | −1.35 (−1.62, −1.07) | −2.4 (−7.5, +2.8) | |
| 10v2 | 0/0/12 | −472.2 (−591.0, −345.4) | −3.38 (−4.43, −2.24) | −2.6 (−10.4, +5.8) |
Qué muestran los registros de entrenamiento
Fracción de ticks en que el NEAT tenía un enemigo percibido con línea de visión, alineación media de la mira y daño por episodio en el entorno de entrenamiento (10 NEAT + 2 BT). Se compara el inicio (generaciones 0 a 2) con las últimas 10.
| Corrida | Visión (inicio → final) | Alineación de mira | Daño por episodio | Precisión de disparo |
|---|---|---|---|---|
| run12, visión completa, con semilla | 0.55 → 0.52 | 0.81 → 0.83 | 74 → 80 | 0.033 → 0.045 |
| Control, visión completa, sin semilla | 0.58 → 0.52 | 0.28 → 0.67 | 14 → 56 | 0.007 → 0.029 |
| Por vista, con semilla | 0.34 → 0.33 | 0.28 → 0.28 | 67 → 83 | 0.026 → 0.034 |
| Por vista, sin semilla | 0.18 → 0.23 | 0.11 → 0.16 | 11 → 19 | 0.005 → 0.006 |
- Bajo percepción por vista el NEAT ve al enemigo un tercio del tiempo con semilla (contra poco más de la mitad con visión completa) y no aprende a verlo más.
- La línea con semilla hace en el entorno de entrenamiento un daño (83) igual al de la línea con visión completa (80), pero contra el BT queda lejos (71.5 contra 161.2 en 3v3). En ese entorno la mayoría de los oponentes son otros NEAT con la misma limitación.
- La línea sin semilla no aprende: su alineación de mira queda en 0.16, frente a 0.67 del control con visión completa.
Control de diagnóstico: reflejo con búsqueda (9003)
Para comprobar si la falta de búsqueda explica el resultado, se escribió a mano una variante del reflejo con retroceso que gira de forma constante cuando no ve al enemigo (sesgo de giro +0.3 que se anula al percibirlo). La predicción, anotada antes de correrla: si la búsqueda fuera el cuello de botella, mejoraría de forma clara sobre 9002. Resultado por vista en 3v3 (20 partidas): daño 22.1 contra 162.2 del BT, bajas 0.13 contra 0.90, vivo 40.9 contra 23.4 s; Δ daño −140.1 (−164.4, −116.0), victorias 2/1/17. Es peor que 9002 (−98.2). La predicción no se cumple con una búsqueda tan simple. No descarta otras formas de búsqueda ni de memoria.
Velocidad medida
Velocidad horizontal media de los bots mientras viven (cm/s, tope de caminar 600), en las partidas 3v3 de 90 s con percepción por vista, 10 partidas por controlador (30 bots NEAT y 30 BT).
| Controlador | NEAT (cm/s) | BT en esas partidas (cm/s) | Tiempo vivo NEAT (s) | Daño NEAT |
|---|---|---|---|---|
| Reflejo con retroceso a mano (9002) | 12.3 | 405.6 | 47.2 | 46.8 |
| Etapa 2 de réplica 1 (5768) | 17.4 | 402.8 | 42.5 | 27.3 |
| Etapa 2 de run12 (7837) | 19.9 | 403.3 | 44.1 | 46.2 |
| run12 g100 (5582) | 67.6 | 390.2 | 54.6 | 61.8 |
| Reflejo a mano (9001) | 98.8 | 402.7 | 30.4 | 54.0 |
| Reflejo con búsqueda a mano (9003) | 271.4 | 390.9 | 50.2 | 14.1 |
| Entrenado por vista, con semilla (3332) | 338.1 | 408.9 | 19.2 | 74.4 |
| Control sin semilla g100 (5587) | 525.9 | 404.8 | 41.2 | 13.2 |
| Entrenado por vista, sin semilla (5598) | 575.7 | 491.4 | 48.9 | 7.8 |
- Los controladores que hacen más daño y sobreviven más (los reflejos y los campeones de etapa 2) casi no se mueven: de 12 a 99 cm/s, entre el 2 y el 16 % del tope de 600.
- Los que corren a casi toda velocidad (526 y 576 cm/s) hacen poco daño (13 y 8).
- El BT se mueve a ≈390–490 cm/s en todas las partidas, entre el 65 y el 80 % del tope.
- La interpretación anterior, «retrocede a media velocidad», salía del sesgo de avance de la red (−0.21 daría una entrada de −0.48). La medición indica que casi no se mueve. La causa no está confirmada: una hipótesis es que retrocede hasta una pared y se queda disparando. No se registran posiciones.
- Esta medición es solo bajo percepción por vista. Con visión completa no se midió porque la instrumentación se compiló después.
Lo que implica: la táctica que la evolución y el reflejo a mano comparten se parece a una torreta: apuntar y disparar sin desplazarse. Contra ella, el BT se acerca con mira instantánea. Si un jugador humano percibiría ese comportamiento como campear o como parte natural del combate no está medido.
Qué pudo haber pasado
| Hipótesis | Evidencia | Estado |
|---|---|---|
| Error de programación del filtro de visión | La prueba de humo dio 0 de 495 muestras con enemigo sin visión (con la observación original, 205 de 498). El cono usa el coseno del ángulo, y el motor lo aplica como semiángulo (AISense_Sight.cpp), igual que el BT. Las corridas por vista se comportan distinto (visión 0.33 contra 0.52). | Improbable, no demostrado falta comparar el trazo de visión del NEAT con el del BT |
| Error en las corridas o en la infraestructura | 0 fallos y 0 trazas de error en el entrenamiento; mismo protocolo y mismos escenarios; las mediciones de los campeones previos se hicieron en la misma cola. | Sin indicios |
| Diseño: sin búsqueda ni memoria | El NEAT ve al enemigo un tercio del tiempo y no mejora eso en 60 a 100 generaciones. El control con búsqueda a mano (9003) rinde peor que sin ella. | Contribuye, no basta |
| Ventajas del BT | Mira instantánea hacia el objetivo percibido y navegación hacia el enemigo más cercano del mapa. El daño que hace el BT sube cuando el NEAT pierde la información (por ejemplo, de 87 a 136 contra el campeón 7837). | Probable contribuyente grande no separable sin otro experimento |
| Entorno de entrenamiento no presiona contra el BT | La línea por vista con semilla iguala en el entrenamiento a la de visión completa (daño 83 contra 80) pero queda lejos del BT. La etapa 2 (5 NEAT + 5 BT) fue la que mejoró contra el BT con visión completa, y se aplicó después a la línea por vista (E1, 40 generaciones): el Δ de daño en 3v3 pasó de −89.7 a −84.8, con intervalos que se solapan; en 5v5 y 10v2 quedó peor. La velocidad media del campeón de E1 es de 342 cm/s (BT 412). | No respaldada con 40 generaciones la etapa 2 por vista no mejoró |
| Presupuesto e hiperparámetros | 60 a 100 generaciones, población 60, mutación suave calibrada con visión completa, fitness no recalibrado, una corrida por línea. | No descartable |
| «NEAT no sirve para este proyecto» | Con visión completa evolucionó un controlador que empata al BT en 3v3, aunque uno de tres reglas escrito a mano lo supera. Con percepción por vista no alcanza a un BT que ganó 28 de 33 sesiones a jugadores humanos y que tiene ventajas de mira y de navegación. | Sin base lo observado no permite concluirlo |
Lo que sí se puede decir: con esta observación, estas salidas, este presupuesto y percepción comparable a la humana, la evolución no produjo un controlador que alcance al BT de Lyra. Eso es un resultado negativo documentado con una condición de partida (visión completa) que explica por qué antes parecía lo contrario.
Qué cambia en las conclusiones
- «NEAT iguala al BT» deja de ser una conclusión general. Es cierto bajo visión completa, en 3v3, y no bajo percepción por vista.
- Se mantiene: el método y sus validaciones, el valor de la información medido (70 a 140 puntos de daño relativo), la supervivencia mayor de los reflejos que casi no se mueven y el hallazgo de que la táctica evolucionada la reproduce una regla escrita a mano.
- El diseño de la solicitud (sensores de vector al enemigo y línea de visión) no pedía igualdad de información con el BT. La comparación justa la define este trabajo.
Correcciones registradas
Estas son afirmaciones hechas durante el trabajo y retiradas o ajustadas cuando los datos las contradijeron. Se dejan por escrito porque forman parte del método.
| Afirmación inicial | Qué se encontró |
|---|---|
| El mapa no tiene fase Playing | Sí la tiene: Warmup de ≈30 s y luego Playing. Varias corridas tempranas estaban contaminadas. |
| El fitness premia esconderse (n=2, luego n=20) | La calibración con 40 episodios lo desmintió. El orden de referencias es correcto con cualquier peso de 0 a 0.5. |
| Ver al enemigo equivale a recibir daño | No es un argumento válido: la correlación observada depende de cómo dispara el BT. |
| La fiabilidad subió de 0.09 a 0.7, la medición mejoró | Engañoso: depende de la diversidad de la población. |
| Con el umbral calibrado habrá ≈10 especies | En las corridas finales hubo 2. |
| La etapa 2 supera ligeramente al BT en daño y bajas | No se sostiene con los intervalos para run12. Sí en la réplica, en 3v3 y 5v5. |
| Se usó el plugin Learning Agents | No se usó. Se analizó y se implementó una integración nativa por archivos. La solicitud lo prometía; se declara como desviación. |
| Mismos escenarios y posiciones de inicio para todos los genomas | No implementado: solo el identificador de escenario es común. Es una fuente de ruido. |
| Hipótesis de mutaciones agresivas como única causa | Estaba confundida con la deriva del giro vertical; las causas eran dos. |
| El campeón retrocede a media velocidad (por su sesgo de avance de −0.21) | La velocidad medida es de 12 a 68 cm/s en los reflejos y campeones de etapa 2 (BT ≈400). Casi no se mueven. La causa no está confirmada; no se registran posiciones. |
| La comparación contra el BT es en igualdad de información | No: el NEAT tenía visión a través de paredes (corregido, ver la siguiente sección). |
Validez y asimetrías
La condición nueva, «percepción por vista», entrega al NEAT el enemigo más cercano que cumpla tres cosas: línea de visión, ángulo horizontal de ±60° y distancia ≤2500 uu, los valores del BT. No tiene memoria, igual que el BT. La prueba de humo mostró 0 de 495 muestras con enemigo sin visión. La condición cambia el significado de dos términos del fitness: Fapunta y Fcerca solo cuentan con enemigo percibido.
Asimetrías que permanecen, por orden de importancia:
- Búsqueda. Sin objetivo percibido, el BT navega hacia el enemigo vivo más cercano del mapa (
EQS_GetAllEnemyrecorre todos los personajes del equipo contrario sin filtro de visión). El NEAT no tiene ni navegación ni conocimiento de posiciones. - Mira. La rotación de control del BT se ajusta al instante al objetivo percibido; el NEAT gira como máximo 180°/s en horizontal y no tiene giro vertical.
- Navegación y recarga. El BT usa navmesh, EQS, recarga y búsqueda de armas. El NEAT tiene tres rayos de obstáculos.
- Frecuencia. El NEAT decide a 10 Hz; el BT actualiza su objetivo cada ≈0.5 s (ventaja del NEAT en tiempo de reacción).
- Velocidad de movimiento. En las sesiones humanas se preguntó por qué el bot podía correr y los jugadores no. Lo verificado: Lyra no define un sprint en su código C++; el personaje (
B_Hero_ShooterMannequin) tieneMaxWalkSpeed600 uu/s (300 agachado) y velocidad analógica mínima de 200; el Gym usa los mismos datos de personaje (HeroData_AVE) y el mismo personaje para jugadores y bots. El conjunto de habilidades del Gym (AbilitySet_AVE) no incluye salto, dash, cuerpo a cuerpo, granada, soltar arma ni emote, que sí están en el de Lyra, aunque el mapa de entradas del jugador todavía los enlaza; eso pudo percibirse como que no podían correr. El apuntado con mira reduce la velocidad de los jugadores. Por configuración no hay diferencia de velocidad máxima entre humanos y bots. La velocidad real se midió después en los bots (sección «Velocidad medida»); en los humanos falta, porque la columnaspeedde la telemetría se compiló después de las sesiones. - Sentido de daño. El BT lo tiene configurado. No se halló quién lo reporta en el C++; falta revisar los Blueprints.
Otras limitaciones: una sola corrida por condición sin semilla y dos con semilla, de modo que no hay variabilidad estimable; el campeón se elige entre 10 candidatos (maldición del ganador, parcialmente corregida con escenarios nuevos); la cobertura no está validada; el fitness no se recalibró bajo la nueva condición.
Solicitud contra realidad
| Lo prometido | Lo realizado | Estado |
|---|---|---|
| Controlador NEAT que reemplace al BT en NPCs de Lyra | Controlador nativo en C++ integrado a Lyra y evaluado contra el BT | Hecho |
| Puente C++/Python con Learning Agents | Integración propia por archivos JSON, evaluación por lotes | Desviación |
| Sensores: distancia al enemigo, salud, línea de visión | 13 entradas que incluyen esas tres; en la condición nueva, restringidas a lo visible | Hecho |
| Población de 50 a 100 genomas | 60 | Hecho |
| Fitness: premiar daño y supervivencia, penalizar daño recibido | Esa forma, más una ayuda de puntería que se apaga en la generación 30 | Hecho con cambios |
| Benchmark: tasa de victoria, precisión, uso de cobertura | Tasa de victoria con intervalo, precisión medida, cobertura medida con definición operacional | Parcial |
| Población inicial de redes mínimas con pesos aleatorios (cartel del congreso) | La línea sin semilla; los mejores resultados usan una semilla de reflejo | Declarar |
| Sesiones nuevas contra jugadores humanos (cartel del congreso) | No realizadas | Pendiente |
| Reporte de máximo 4 cuartillas y cartel de 90 × 120 cm | Borrador de cartel; este documento es la fuente para el reporte | En curso |
Qué sigue
La cola night4.py terminó el 9 de oct a las 09:20 sin fallos. Quedan abiertas pruebas que separan las explicaciones de la sección anterior. La cola night5.py las ejecuta en orden desde las 12:50; han terminado el control 9003, la medición de velocidad y E1; corre ahora E2 (réplica de la línea por vista con semilla).
| Prueba | Qué separa | Costo aprox. |
|---|---|---|
| Etapa 2 (40 generaciones, 5 NEAT + 5 BT) sobre la línea por vista con semilla (terminó a las 16:40: sin mejora clara, ver «Resultados bajo percepción por vista») | Si la falta de presión contra el BT en el entrenamiento explica la caída | ≈3.7 h |
| Réplicas de las líneas por vista con otras semillas | Cuánta variación hay entre corridas (hoy n=1 por línea) | 2–5 h cada una |
Medir la velocidad real de jugadores humanos (la columna speed ya está compilada) | Si la referencia humana tiene una ventaja de movimiento | 1 h de partidas |
| Partidas jugadas por una persona contra los campeones | Si la táctica de retroceso funciona contra humanos | 1–2 h del jugador |
Criterio de reporte fijado antes de ver datos: se reportan todos los resultados, sean buenos o malos, sin umbral de éxito.
Decisiones que requieren la opinión de los doctores
- Dr. Cuéllar: validar la definición operacional de cobertura y el esquema de comparaciones (tasa de victoria por partida, intervalos por remuestreo, tamaño de efecto solo exploratorio).
- Dra. Ponce de León: el papel de la semilla de reflejo frente a la población inicial mínima aleatoria.
- Ambos: el marco de igualdad. Propuesta: para el miniproyecto, igualdad con el bot de Lyra en percepción; para el congreso, percepción comparable a la de un jugador humano, con partidas jugadas contra el campeón si hay tiempo.
Archivos de respaldo
FITNESS_CHANGELOG.md: cada cambio de diseño con su evidencia y lo que no se afirma.night_log.md,runs/overnight_log.txt: bitácora de las guardias.informe_manana.md,runs/estadistica_comparaciones.md: resultados y estadística.CALIBRATION_PROTOCOL.md,AUDIT_PROTOCOL.md,AB_PITCH_PROTOCOL.md,STAGE2_PROTOCOL.md,NIGHT2_PROTOCOL.md,FAIR_PROTOCOL.md: protocolos escritos antes de ver datos.AUDITORIA_PLAN_VS_REALIDAD.md: comparación punto por punto con el plan del 1 de oct.
Participación de los alumnos inscritos en la solicitud: Leslie Benitez y Noé de Luna, por documentar por el responsable.