Undertale In Extremis
Proyecto para Organización de Computadores. Simulador de batalla RPG en Assembly Risc-V estilo Undertale.
Proyecto universitario (ORG 2026.1). Un simulador de combate RPG y motor Monte Carlo escrito completamente en ensamblador RISC-V.
La Configuración
Construí un motor de combate RPG desde cero en ensamblador RISC-V puro. El objetivo era escribir tres máquinas de estado de IA distintas, lanzarlas en un emulador sin interfaz gráfica y ejecutar 10,000 partidas automatizadas para ver qué estrategia resultaba ganadora.
El combate gira en torno a una economía de acciones estricta. El MP se regenera lentamente, pero puede sobrecargarse usando habilidades específicas.
| Habilidad | Costo | Qué hace |
|---|---|---|
| Ataque | gratis | Tira un 1d20. < 10 falla, 10+ acierta, 20 crítico que duplica el daño. |
| Defender | gratis | Bloquea el daño entrante. Las tiradas altas provocan un contraataque. |
| Valor Absoluto | 20 MP | Evita los dados para un golpe crítico garantizado. |
| Robo de Alma | gratis | El lanzador recibe 1–12 de daño de retroceso, drena esa misma cantidad del MP del enemigo y gana 4× esa cantidad para sí mismo. La única forma de superar el límite de 100 MP. |
| Ejecución Final | 150 MP | Un ataque nuclear de 800% de daño. Falla si el objetivo tiene más del 50% de HP. |
| Escudo Espejo | 30 MP | Refleja el próximo ataque entrante de vuelta al atacante. |
Los Contendientes
Escribí tres bots, cada uno con un cerebro completamente diferente:
Flowey (El Caos): RNG puro. No evalúa nada y solo tira un número aleatorio para elegir su próximo movimiento.
decision_random:
li a0, 6
call randomizer # elige un número entre 1 y 6, esa es toda la estrategia
j decision_end
Chara (La Esforzada): Optimiza para un combo: spamear Robo de Alma hasta alcanzar 150 MP, reducir al enemigo por debajo del 50% de HP y lanzar la Ejecución Final. En el código, la estrategia también se llama inteligente, pero no lo es tanto.
decision_smart:
# eu escrevi essa estrategia
# ela se consiste em usar roubo de alma até poder usar execute
# só que pra isso também precisamos sobreviver e diminuir a vida do inimigo pra 50%
# sem morrer
...
li t6, 150 # precio del execute
blt t4, t6, decision_smart_my_mana_is_low # ¿mp < 150? ve a farmear
bge a2, t6, decision_smart_enemy_hp_high # ¿hp enemigo > 50? ve a acosar
j decision_smart_i_can_kill # de lo contrario, ejecuta
decision_smart_my_mana_is_low:
li a0, 4 # robo de alma
decision_smart_enemy_hp_high:
li a0, 2 # valor absoluto
decision_smart_i_can_kill:
li a0, 5 # ejecución final
Toby (El Contraataque): Construido específicamente para farmear a Chara. Vigila tanto su propio HP como la barra de MP del enemigo. Si está por debajo de 50 HP y el enemigo tiene 150 MP, levanta su Escudo Espejo y deja que el ataque nuclear regrese. Fuera de esa ventana, combina ataques y Robo de Alma, y también puede lanzar su propia Ejecución Final cuando las condiciones se alinean.
decision_troll_checks:
li t6, 50
ble t5, t6, decision_troll_check_enemy_mp # ¿estoy lo suficientemente bajo como para preocuparme?
j decision_troll_not_execute
decision_troll_check_enemy_mp:
li t6, 150
bge a3, t6, decision_troll_prepare_against_execute # ¿está el enemigo cerca de 150 mp?
j decision_troll_not_execute
decision_troll_prepare_against_execute:
li a0, 6 # escudo espejo
ret
Los Resultados del Benchmark
Después de ejecutar 10,000 partidas en Terminal usando rars.jar, el jar del simulador basado en Java RISC-V RARS, los resultados llegaron.
| Personaje | Tasa de Victorias |
|---|---|
| Flowey | ~52% |
| Toby | ~29% |
| Chara | ~17% |
La IA más tonta ganó la mayoría absoluta de las partidas.
La tasa de victorias del 17% de Chara expone el problema de los combos rígidos y codiciosos. Para alcanzar 150 MP, tiene que recibir daño de retroceso de Robo de Alma. A menudo, Toby solo levanta su escudo, y el script de Chara la obliga a seguir drenando su propia salud hasta que literalmente se mata a sí misma antes de poder lanzar su habilidad definitiva.
El 29% de Toby proviene de atrapar exitosamente a Chara. Contra Flowey es otra historia: la condición del escudo requiere que el enemigo esté cerca de 150 MP, y Flowey nunca construye deliberadamente hacia eso. El contraataque nunca se activa, así que Toby solo juega su juego predeterminado de atacar y robar almas, lo que no le da una ventaja real sobre el caos.
Flowey ganó el 52% de las veces porque no tener estrategia es imposible de contrarrestar de manera consistente. Nunca recibe daño de retroceso tratando de preparar una jugada masiva; simplemente lanza movimientos de alto valor por accidente. Resulta que, si toda tu base de código depende de predecir el comportamiento del enemigo, automáticamente pierdes contra un enemigo que hace cosas sin razón.
¿Es Esto Realmente Sorprendente?
Probablemente no. Que los agentes aleatorios dominen a los deterministas es un resultado bien documentado en simulaciones de estrategia.
Una simulación de Citadel que encontré en un grupo de estudio produjo el mismo patrón: algunas estrategias vencían consistentemente al azar, otras eran aplastadas por él, y algunas vencían a ciertos oponentes pero perdían contra otros. La dinámica de piedra-papel-tijera estaba ahí, pero el azar aún se mantenía firme contra la mayoría de ellas.
La diferencia es que en un entorno más rico (más estrategias, más variables de decisión, más formas para que un agente inteligente explote a uno aleatorio) los resultados tienden a dispersarse más. Una estrategia determinista bien ajustada puede labrarse una ventaja confiable si el espacio de acciones le da suficiente con qué trabajar.
Aquí, probablemente no. Con solo seis acciones posibles y un sistema de combate donde un solo crítico afortunado puede terminar la partida independientemente de la estrategia, la brecha entre la planificación cuidadosa de Chara y el aporreo aleatorio de botones de Flowey simplemente no es tan amplia. El combo de Chara requiere varios turnos de preparación, y el RNG tiene amplia oportunidad de matarla antes de que llegue allí. El espacio de acciones puede ser simplemente demasiado pequeño y demasiado volátil para que una estrategia codiciosa supere consistentemente al caos.
En otras palabras: que Flowey gane es menos un hallazgo y más una restricción de diseño. Una Chara más inteligente necesitaría un juego más inteligente para demostrarlo.
Por Qué Estos Números No Son Totalmente Confiables
Antes de sacar conclusiones del benchmark, vale la pena reconocer algunos sesgos estructurales.
Los enfrentamientos no están aislados. Ambos jugadores reciben una estrategia asignada aleatoriamente en cada partida, lo que significa que las tasas de victoria son agregados de todos los emparejamientos posibles, no estadísticas limpias de 1v1. El 52% de Flowey incluye partidas donde Flowey luchó contra otro Flowey, y en esas uno de ellos tenía que ganar. Para saber realmente si Chara vence a Toby o si Flowey vence a todos por igual, necesitarías fijar el enfrentamiento y ejecutar cada emparejamiento por separado.
El orden de turnos nunca se rota. El Jugador 1 siempre se mueve primero. En un sistema donde un solo crítico puede terminar una partida, ir primero es una ventaja real. Los resultados no tienen esto en cuenta en absoluto.
El RNG es un xorshift personalizado iniciado con la hora del sistema. Funciona lo suficientemente bien para un proyecto universitario, pero no es un generador estadísticamente validado. Si la distribución de resultados es desigual entre las 6 acciones posibles, los resultados de Flowey se ven directamente afectados, ya que toda su estrategia consiste en llamar a esa función.
El espacio de acciones es muy pequeño. Seis acciones posibles significa que cualquier estrategia tiene un margen limitado para diferenciarse del caos. En un sistema más profundo, el combo de Chara podría ser más difícil de interrumpir, o podría haber opciones de recuperación que reduzcan el costo de su preparación. Aquí, el juego es lo suficientemente castigador como para que su daño de retroceso a menudo termine la partida antes de que llegue la recompensa.
Chara probablemente no fue diseñada lo suficientemente bien. Su estrategia no tiene un plan de defensa. Si las condiciones del combo no se cumplen y está recibiendo mucho daño, sigue farmeando MP de todos modos. Una versión más robusta cambiaría al modo de supervivencia cuando estuviera por debajo de cierto umbral de HP, lo que probablemente mejoraría sus números significativamente.
Los resultados son direccionalmente interesantes, pero deben leerse como una instantánea de esta configuración específica, no como una declaración general sobre estrategia vs. aleatoriedad.
Investigación Futura
El siguiente paso obvio es salir completamente de RARS. Esta versión se ejecuta en un emulador RISC-V basado en Java, lo que pone un límite duro a la velocidad de simulación. Una segunda versión dirigida a RISC-V compilado real con syscalls de Linux en lugar de ecalls de RARS debería ser dramáticamente más rápida, lo que importa mucho una vez que el recuento de partidas comienza a subir a millones.
Más allá del rendimiento, las direcciones más interesantes están en el lado del diseño:
Más jugadores por partida. Ahora mismo siempre es 1v1. Agregar un tercer o cuarto participante cambia completamente el panorama estratégico. De repente, una contraestrategia tiene que considerar ser atacada desde dos direcciones a la vez, y la aleatoriedad pura se vuelve más difícil de mantener.
Aprendizaje automático dentro del ensamblador. La idea es implementar un algoritmo de ML mínimo directamente en RISC-V, usando operaciones matriciales para permitir que un bot actualice sus propios pesos basándose en los resultados de las partidas. Sin bibliotecas externas, sin tiempo de ejecución de alto nivel, solo matemática matricial con enteros en registros. Si esto es práctico o solo un dolor interesante de implementar es parte del atractivo.
Más estrategias y un motor más profundo. El espacio de acciones actual es demasiado pequeño para que cualquier estrategia se distancie significativamente del azar. Tomar más inspiración directa de World of Warcraft (enfriamientos, compensaciones de recursos, efectos de posicionamiento, interacciones de habilidades más variadas) daría a las estrategias bien diseñadas un espacio real para demostrar su valía sobre el caos.
La infraestructura del benchmark ya está ahí. El cuello de botella es que el juego sea lo suficientemente profundo para que los resultados signifiquen algo.
Cómo Ejecutarlo
Para compilar el ensamblador y ejecutar el motor localmente:
make simulate
