Undertale In Extremis
Progetto per Organizzazione di Computer (Organização de Computadores). Simulatore di battaglia RPG in Assembly Risc-V simile a Undertale.
Progetto universitario (ORG 2026.1). Un simulatore di combattimento GDR e motore Monte Carlo scritto interamente in assembly RISC-V.
L'Impostazione
Ho costruito un motore di combattimento GDR da zero in puro assembly RISC-V. L'obiettivo era scrivere tre macchine a stati AI distinte, lanciarle in un emulatore headless ed eseguire 10.000 partite automatizzate per vedere quale strategia sarebbe emersa vincitrice.
Il combattimento ruota attorno a un'economia di azione rigorosa. L'MP si rigenera lentamente, ma può essere sovraccaricato usando abilità specifiche.
| Abilità | Costo | Cosa fa |
|---|---|---|
| Attacco | gratuito | Tira un 1d20. < 10 manca, 10+ colpisce, 20 critico per danno doppio. |
| Difesa | gratuito | Blocca il danno in arrivo. Tiri alti innescano un contrattacco. |
| Grinta Assoluta | 20 MP | Ignora il dado per un colpo critico garantito. |
| Succhia-Anima | gratuito | Il lanciatore subisce 1–12 danni da contraccolpo, drena la stessa quantità dall'MP nemico e ne guadagna 4× per sé. L'unico modo per superare il limite di 100 MP. |
| Esecuzione Finale | 150 MP | Un colpo nucleare con danno 800%. Fallisce se il bersaglio è sopra il 50% di HP. |
| Scudo Specchio | 30 MP | Riflette il prossimo attacco in arrivo verso l'attaccante. |
I Concorrenti
Ho scritto tre bot, ciascuno con un cervello completamente diverso:
Flowey (Il Caos): Puro RNG. Non valuta nulla e tira semplicemente un numero casuale per scegliere la sua prossima mossa.
decision_random:
li a0, 6
call randomizer # sceglie un numero tra 1 e 6, questa è l'intera strategia
j decision_end
Chara (La Sforzata): Ottimizza per una combo: spamma Succhia-Anima finché non raggiunge 150 MP, riduce la salute del nemico sotto il 50% HP e scatena Esecuzione Finale. Nel codice la strategia è anche chiamata smart, ma non lo è poi 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 # prezzo dell'esecuzione
blt t4, t6, decision_smart_my_mana_is_low # mp < 150? vai farmare
bge a2, t6, decision_smart_enemy_hp_high # hp nemico > 50? vai fare prepotenza
j decision_smart_i_can_kill # altrimenti, esegui
decision_smart_my_mana_is_low:
li a0, 4 # succhia-anima
decision_smart_enemy_hp_high:
li a0, 2 # grinta assoluta
decision_smart_i_can_kill:
li a0, 5 # esecuzione finale
Toby (Il Contro-Specifico): Costruito specificamente per farmare Chara. Osserva sia i propri HP che la barra MP del nemico. Se è sotto 50 HP e il nemico ha 150 MP, alza il suo Scudo Specchio e lascia che il colpo nucleare torni indietro. Al di fuori di questa finestra, alterna attacchi e Succhia-Anima, e può anche scatenare Esecuzione Finale quando le condizioni sono allineate.
decision_troll_checks:
li t6, 50
ble t5, t6, decision_troll_check_enemy_mp # sono abbastanza basso da preoccuparmi?
j decision_troll_not_execute
decision_troll_check_enemy_mp:
li t6, 150
bge a3, t6, decision_troll_prepare_against_execute # il nemico è vicino a 150 mp?
j decision_troll_not_execute
decision_troll_prepare_against_execute:
li a0, 6 # scudo specchio
ret
I Risultati del Benchmark
Dopo aver eseguito 10.000 partite su Terminal usando rars.jar, il jar del simulatore RISC-V basato su Java RARS, i risultati sono arrivati.
| Personaggio | Tasso di Vittoria |
|---|---|
| Flowey | ~52% |
| Toby | ~29% |
| Chara | ~17% |
L'AI più stupida ha vinto la maggioranza assoluta delle partite.
Il tasso di vittoria del 17% di Chara espone il problema delle combo rigide e avide. Per raggiungere 150 MP, deve subire danni da contraccolpo da Succhia-Anima. Spesso Toby alza semplicemente il suo scudo, e lo script di Chara la costringe a continuare a drenare la propria salute finché non si uccide letteralmente prima ancora di poter lanciare la sua abilità finale.
Il 29% di Toby deriva dall'aver attirato con successo Chara. Contro Flowey è un'altra storia: la condizione dello scudo richiede che il nemico sia vicino a 150 MP, e Flowey non costruisce mai deliberatamente verso quello. Il contrattacco non si innesca mai, quindi Toby gioca semplicemente la sua partita predefinita di attacchi e succhia-anima, che non gli dà alcun vero vantaggio sul caos.
Flowey ha vinto il 52% delle volte perché non avere alcuna strategia è impossibile da contrastare in modo coerente. Non subisce mai danni da contraccolpo cercando di preparare una mossa massiccia; lancia semplicemente mosse di alto valore per caso. Si scopre che, se l'intera base di codice si basa sulla previsione del comportamento nemico, si perde automaticamente contro un nemico che fa cose senza motivo.
È Effettivamente Sorprendente?
Probabilmente no. Gli agenti casuali che dominano quelli deterministici sono un risultato ben documentato nelle simulazioni strategiche.
Una simulazione Citadel che ho incontrato in un gruppo di studio ha prodotto lo stesso schema: alcune strategie battevano costantemente il casuale, alcune venivano schiacciate da esso, e alcune battevano certi avversari ma perdevano contro altri. La dinamica sasso-carta-forbici era presente, ma il casuale ha comunque tenuto testa alla maggior parte di esse.
La differenza è che in un ambiente più ricco (più strategie, più variabili decisionali, più modi per un agente intelligente di sfruttare uno casuale) i risultati tendono a distribuirsi maggiormente. Una strategia deterministica ben calibrata può ritagliarsi un vantaggio affidabile se lo spazio delle azioni le dà abbastanza con cui lavorare.
Qui, probabilmente non lo fa. Con solo sei azioni possibili e un sistema di combattimento dove un singolo critico fortunato può concludere la partita indipendentemente dalla strategia, il divario tra la pianificazione attenta di Chara e la pressione casuale di pulsanti di Flowey semplicemente non è così ampio. La combo di Chara richiede diversi turni di preparazione, e l'RNG ha ampia opportunità di ucciderla prima che ci arrivi. Lo spazio delle azioni potrebbe essere semplicemente troppo piccolo e troppo altalenante perché una strategia avida possa superare costantemente il caos.
In altre parole: la vittoria di Flowey è meno una scoperta e più un vincolo di progettazione. Una Chara più intelligente avrebbe bisogno di un gioco più intelligente per dimostrarlo.
Perché Questi Numeri Non Sono Completamente Affidabili
Prima di trarre conclusioni dal benchmark, ci sono alcuni pregiudizi strutturali che vale la pena riconoscere.
Gli scontri non sono isolati. Entrambi i giocatori ricevono una strategia assegnata casualmente ogni partita, il che significa che i tassi di vittoria sono aggregati su tutte le possibili combinazioni, non statistiche pulite 1v1. Il 52% di Flowey include partite in cui Flowey ha combattuto un altro Flowey, e in quelle uno di loro doveva vincere. Per sapere effettivamente se Chara batte Toby o se Flowey batte tutti allo stesso modo, bisognerebbe fissare lo scontro ed eseguire ogni combinazione separatamente.
L'ordine di turno non viene mai ruotato. Il Giocatore 1 si muove sempre per primo. In un sistema dove un singolo critico può concludere una partita, muovere per primo è un vero vantaggio. I risultati non tengono conto di questo.
L'RNG è un xorshift personalizzato seedato dal tempo di sistema. Funziona abbastanza bene per un progetto universitario, ma non è un generatore statisticamente validato. Se la distribuzione degli output è disomogenea tra le 6 azioni possibili, i risultati di Flowey sono direttamente influenzati poiché la sua intera strategia consiste nel chiamare quella funzione.
Lo spazio delle azioni è molto piccolo. Sei azioni possibili significa che qualsiasi strategia ha spazio limitato per differenziarsi dal caos. In un sistema più profondo, la combo di Chara potrebbe essere più difficile da interrompere, o potrebbero esserci opzioni di recupero che riducono il costo della sua preparazione. Qui, il gioco è giusto abbastanza punitivo che il suo danno da contraccolpo spesso conclude la partita prima che il guadagno arrivi.
Chara probabilmente non è stata progettata abbastanza bene. La sua strategia non ha un ripiego difensivo. Se le condizioni della combo non sono soddisfatte e sta subendo gravi danni, continua comunque a farmare MP. Una versione più robusta passerebbe alla modalità sopravvivenza quando sotto una certa soglia di HP, il che probabilmente migliorerebbe significativamente i suoi numeri.
I risultati sono direzionalmente interessanti ma dovrebbero essere letti come un'istantanea di questa configurazione specifica, non come un'affermazione generale su strategia vs casualità.
Ricerca Futura
Il passo successivo ovvio è abbandonare completamente RARS. Questa versione funziona su un emulatore RISC-V basato su Java, che pone un tetto massimo alla velocità di simulazione. Una seconda versione mirata a RISC-V compilato reale con syscall Linux invece di ecall RARS dovrebbe essere drammaticamente più veloce, il che conta molto una volta che il conteggio delle partite inizia a salire verso i milioni.
Oltre alle prestazioni, le direzioni più interessanti sono sul lato progettuale:
Più giocatori per partita. Al momento è sempre 1v1. Aggiungere un terzo o quarto partecipante cambia completamente il panorama strategico. Improvvisamente una contro-strategia deve tenere conto di essere attaccata da due direzioni contemporaneamente, e la pura casualità diventa più difficile da sostenere.
Apprendimento automatico all'interno dell'assembly. L'idea è implementare un algoritmo ML minimale direttamente in RISC-V, usando operazioni su matrici per permettere a un bot di aggiornare i propri pesi basandosi sui risultati delle partite. Nessuna libreria esterna, nessun runtime di alto livello, solo matematica di matrici intere nei registri. Se questo sia pratico o solo un interessante dolore da implementare fa parte del fascino.
Più strategie e un motore più profondo. L'attuale spazio delle azioni è troppo piccolo perché qualsiasi strategia possa significativamente staccarsi dal casuale. Prendere più ispirazione diretta da World of Warcraft (tempi di recupero, compromessi sulle risorse, effetti di posizionamento, interazioni di abilità più varie) darebbe alle strategie ben progettate spazio reale per dimostrarsi superiori al caos.
L'infrastruttura di benchmark è già lì. Il collo di bottiglia è che il gioco sia abbastanza profondo da rendere i risultati significativi.
Esecuzione
Per compilare l'assembly ed eseguire il motore localmente:
make simulate
