Undertale In Extremis
TeX

Undertale In Extremis

Проект для організації комп'ютерів (Організація комп'ютерів). Симулятор боїв RPG у стилі Undertale на Assembly Risc-V.

GitHub|

Університетський проєкт (ОРГ 2026.1). Симулятор боїв RPG та двигун Монте-Карло, написаний повністю на асемблері RISC-V.

Налаштування

Я створив бойовий двигун RPG з нуля на чистому асемблері RISC-V. Мета полягала в тому, щоб написати три різні машини станів AI, запустити їх у емуляторі без інтерфейсу та провести 10 000 автоматизованих матчів, щоб побачити, яка стратегія виявиться найкращою.

Бій обертається навколо суворої економіки дій. MP регенерується повільно, але його можна перезарядити за допомогою спеціальних навичок.

Навичка Вартість Що робить
Атака безкоштовно Кидає 1d20. < 10 промах, 10+ влучання, 20 критичне влучання з подвійною шкодою.
Захист безкоштовно Блокує вхідну шкоду. Високі кидки запускають контратаку.
Абсолютна Стійкість 20 MP Ігнорує кидки кубиків для гарантованого критичного влучання.
Висмоктування Душі безкоштовно Заклинатель отримує 1–12 шкоди від віддачі, висмоктує таку ж кількість MP у ворога та отримує в 4 рази більше для себе. Єдиний спосіб обійти ліміт у 100 MP.
Остаточне Виконання 150 MP Ядерний удар з 800% шкоди. Не вдається, якщо ціль має більше 50% HP.
Дзеркальний Щит 30 MP Відбиває наступну вхідну атаку назад у нападника.

Змагачі

Я написав трьох ботів, кожен з абсолютно різним мозком:

Флауї (Хаос): Чистий RNG. Він нічого не оцінює і просто кидає випадкове число, щоб вибрати наступний хід.

decision_random:
  li a0, 6
  call randomizer  # обирає число від 1 до 6, це вся стратегія
  j decision_end

Чара (Старається): Вона оптимізує одну комбінацію: спамить Висмоктування Душі, доки не досягне 150 MP, знижує HP ворога нижче 50% і використовує Остаточне Виконання. У коді стратегія також називається "smart", але це не зовсім так.

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        # preço do execute
  blt t4, t6, decision_smart_my_mana_is_low   # mp < 150? go farm
  bge a2, t6, decision_smart_enemy_hp_high    # enemy hp > 50? go bully
  j decision_smart_i_can_kill                 # otherwise, execute

decision_smart_my_mana_is_low:
  li a0, 4  # soul suck
decision_smart_enemy_hp_high:
  li a0, 2  # absolute grit
decision_smart_i_can_kill:
  li a0, 5  # final execution

Тобі (Контр-гра): Створений спеціально для протидії Чарi. Він стежить за своїм HP та MP ворога. Якщо його HP нижче 50, а у ворога 150 MP, він піднімає свій Дзеркальний Щит і дозволяє ядерному удару повернутися. Поза цим вікном він чергує атаки та Висмоктування Душі, а також може сам використовувати Остаточне Виконання, коли умови збігаються.

decision_troll_checks:
  li t6, 50
  ble t5, t6, decision_troll_check_enemy_mp  # am I low enough to worry?
  j decision_troll_not_execute
decision_troll_check_enemy_mp:
  li t6, 150
  bge a3, t6, decision_troll_prepare_against_execute  # is enemy close to 150 mp?
  j decision_troll_not_execute
decision_troll_prepare_against_execute:
  li a0, 6  # mirror shield
  ret

Результати Тестування

Після проведення 10 000 матчів у Terminal за допомогою rars.jar, файлу з симулятора RARS на основі Java для Risc-V, результати були отримані.

Персонаж Коефіцієнт перемог
Флауї ~52%
Тобі ~29%
Чара ~17%

Найдурніший AI виграв переважну більшість ігор.

17% коефіцієнт перемог Чари викриває проблему жорстких, жадібних комбо. Щоб досягти 150 MP, їй доводиться отримувати шкоду від віддачі від Висмоктування Душі. Часто Тобі просто піднімає щит, і скрипт Чари змушує її продовжувати висмоктувати власне здоров'я, доки вона буквально не вб'є себе, перш ніж зможе застосувати свій ультимейт.

29% Тобі отримав завдяки успішному заманюванню Чари. Проти Флауї ситуація інша: умова щита вимагає, щоб ворог був близько до 150 MP, а Флауї ніколи свідомо не накопичує це. Контрзахід ніколи не спрацьовує, тому Тобі просто грає у свою стандартну гру, атакуючи та висмоктуючи душі, що не дає йому реальної переваги над хаосом.

Флауї виграв 52% часу, тому що відсутність стратегії неможливо послідовно передбачити. Він ніколи не отримує шкоду від віддачі, намагаючись підготувати масивний хід; він просто випадково викидає високоцінні ходи. Виявляється, якщо весь ваш код залежить від передбачення поведінки ворога, ви автоматично програєте ворогу, який робить речі без причини.

Чи це насправді дивно?

Ймовірно, ні. Випадкові агенти, що домінують над детермінованими, є добре документованим результатом у симуляціях стратегій.

Симуляція Цитаделі, яку я знайшов у навчальній групі, дала той самий шаблон: деякі стратегії послідовно перемагали випадкові, деякі були розгромлені ними, а деякі перемагали певних супротивників, але програвали іншим. Динаміка "камінь-ножиці-папір" була присутня, але випадковість все ще трималася проти більшості з них.

Різниця в тому, що в багатшому середовищі (більше стратегій, більше змінних прийняття рішень, більше способів для розумного агента експлуатувати випадкового) результати, як правило, розходяться більше. Добре налаштована детермінована стратегія може отримати надійну перевагу, якщо простір дій дає їй достатньо для роботи.

Тут, ймовірно, ні. Маючи лише шість можливих дій та бойову систему, де один щасливий критичний удар може завершити матч незалежно від стратегії, розрив між ретельним плануванням Чари та випадковим натисканням кнопок Флауї просто не такий великий. Комбо Чари вимагає кількох ходів для підготовки, і RNG має достатньо можливостей вбити її до того, як вона досягне мети. Простір дій може бути занадто малим і занадто мінливим для жадібної стратегії, щоб послідовно перевершувати хаос.

Іншими словами: перемога Флауї — це менше відкриття, а більше обмеження дизайну. Розумнішій Чарi знадобилася б розумніша гра, щоб це довести.

Чому ці цифри не повністю надійні

Перш ніж робити висновки з тестування, варто визнати кілька структурних упереджень.

Зіставлення не ізольовані. Обидва гравці отримують випадково призначену стратегію в кожному матчі, що означає, що коефіцієнти перемог є сукупними для всіх можливих пар, а не чистими статистиками 1 на 1. 52% Флауї включають матчі, де Флауї бився з іншим Флауї, і в них одному з них довелося перемогти. Щоб насправді знати, чи Чара перемагає Тобі, чи Флауї перемагає всіх однаково, вам потрібно було б зафіксувати зіставлення та провести кожну пару окремо.

Порядок ходів ніколи не обертається. Гравець 1 завжди ходить першим. У системі, де один критичний удар може завершити матч, перший хід є реальною перевагою. Результати цього зовсім не враховують.

RNG — це користувацький xorshift, заснований на системному часі. Він працює достатньо добре для університетського проєкту, але це не статистично перевірений генератор. Якщо розподіл виходів нерівномірний по 6 можливих діях, результати Флауї безпосередньо впливають, оскільки вся його стратегія полягає в виклику цієї функції.

Простір дій дуже малий. Шість можливих дій означає, що будь-яка стратегія має обмежений простір для відмінності від хаосу. У глибшій системі комбо Чари може бути важче перервати, або можуть бути варіанти відновлення, які зменшують вартість її підготовки. Тут гра просто достатньо каральна, що її шкода від віддачі часто завершує гру до того, як настане винагорода.

Чара, ймовірно, була недостатньо добре розроблена. Її стратегія не має захисного резерву. Якщо умови комбо не виконані, і вона отримує сильні пошкодження, вона все одно продовжує накопичувати MP. Більш надійна версія перейшла б у режим виживання при певному порозі HP, що, ймовірно, суттєво покращило б її показники.

Результати цікаві за напрямком, але їх слід розглядати як знімок цієї конкретної конфігурації, а не як загальне твердження про стратегію проти випадковості.

Майбутні Дослідження

Очевидним наступним кроком є повний відхід від RARS. Ця версія працює на емуляторі RISC-V на базі Java, що ставить жорстку стелю швидкості симуляції. Друга версія, націлена на реальний скомпільований RISC-V з системними викликами Linux замість ecalls RARS, повинна бути значно швидшою, що має велике значення, коли кількість матчів зростає до мільйонів.

Окрім продуктивності, більш цікаві напрямки стосуються дизайну:

Більше гравців у матчі. Зараз це завжди 1 на 1. Додавання третього або четвертого учасника повністю змінює стратегічний ландшафт. Раптом контрстратегія повинна враховувати одночасну атаку з двох напрямків, і чистий випадковість стає важче підтримувати.

Машинне навчання всередині асемблера. Ідея полягає в реалізації мінімального алгоритму ML безпосередньо в RISC-V, використовуючи матричні операції, щоб дозволити боту оновлювати власні ваги на основі результатів матчів. Жодних зовнішніх бібліотек, жодного середовища виконання високого рівня, лише цілочисельна матрична математика в регістрах. Чи практично це, чи просто цікавий біль при реалізації — частина привабливості.

Більше стратегій та глибший двигун. Поточний простір дій занадто малий, щоб будь-яка стратегія могла суттєво випередити випадковість. Беручи більше прямого натхнення з World of Warcraft (таймери відновлення, компроміси ресурсів, ефекти позиціонування, більш різноманітні взаємодії навичок) надасть добре розробленим стратегіям реальний простір для доведення себе над хаосом.

Інфраструктура тестування вже є. Обмежуючим фактором є глибина гри, щоб результати мали значення.

Запуск

Щоб скомпілювати асемблер і запустити двигун локально:

make simulate