[Перевод] Бот, играющий в Castlevania


CastlevaniaBot — это плагин для эмулятора NES Nintaco, который играет в Castlevania. Если запустить его на экране заставки, то плагин пройдёт всю игру от начала до конца. Или же можно запустить его в любом месте игры, чтобы он прошёл её часть.


В этой статье я расскажу, как создал бота, способного пройти Castlevania, и как вы можете создать нечто подобное для любой игры на NES.

В этом проекте не использовалось машинное обучение. Скорее, разработку можно назвать «научением машины». Я знаю, как проходить Castlevania. Сложность заключалась в том, чтобы записать моё знание в компьютерную программу. Результатом стала система, симулирующая тот же процесс принятия решений, который выполняю я с контроллером в руках. Для её создания необходимо было чётко изложить подробные детали физики, управляющей битовым миром Саймона Бельмонта, и всех тактик, необходимых опытному охотнику на вампиров.

У CastlevaniaBot есть доступ к набору стратегий, позволяющих справляться с широким диапазоном ситуаций. Большинство из них предназначено для работы с конкретным типом игровых объектов. Например, существует стратегия борьбы со скелетами, ещё одна для людей-рыб, для разбивания свечей, для собирания сердец, и так далее.

CastlevaniaBot постоянно отслеживает состояние игры и по необходимости переключается между разными стратегиями. В процессе принятия решений используется функция приспособленности, ранжирующая все находящиеся на экране игровые объекты. На вершине списка находится основная цель, а когда она меняется, бот меняет стратегию. Например, CastlevaniaBot может готовиться ударить по свечам, когда в кадр влетает летучая мышь. В зависимости от расстояния до летучей мыши CastlevaniaBot может отреагировать, переключившись со стратегии свечей на стратегию летучих мышей. Уничтожив летучую мышь, он продолжит работать со свечами.

Перед принятием решения о переключении CastlevaniaBot рассматривает текущую цель и стратегию. Если ему это не удастся, он может застрять в бесконечном цикле, переключаясь туда и обратно между двумя целями с равным или почти равным приоритетом. Чтобы избежать этого, при появлении новой основной цели, которая только слегка выше по приоритету текущей цели, CastlevaniaBot может решить продолжить текущую стратегию.

Некоторые стратегии автоматически запускаются при попадании в определённую область. Обычно они предназначены для одновременной обработки нескольких игровых объектов. Например, в коридоре с кучей летающих голов медуз лучше всего просто идти вперёд, а не целиться в каждую голову по отдельности.

Некоторые стратегии могут изменяться в зависимости от текущего вторичного оружия и количества собранных сердец, в частности, в боях с боссами. CastlevaniaBot пытается принять наилучшее решение на основании имеющихся у него инструментов и текущей ситуации.


При работе над подобными проектами я в первую очередь стремлюсь упростить задачу. Я представил, на что была бы похожа Castlevania, если бы на уровнях не было врагов, свечей и собираемых предметов. Всё, что в таком случае было бы нужно — просто дойти от начала до конца уровня. Я задался вопросом — если мне удастся научить бота это делать, то насколько сложно будет уничтожить по пути несколько врагов?

Чтобы научить бота двигаться, мне нужны были тайловые карты фонов и понимание того, как организованы уровни. Игра состоит из шести уровней, каждый из которых заканчивается боссом.

09b644665551ebb424b3813af0205173.png


Каждый уровень разделён на 3 или 4 этапа. Этапы обычно разделены деревянными дверями, которые со скрипом открываются и захлопываются за спиной игрока.

2075fba46c9ef8ccd9a496df6aadcf07.png


Двери являются контрольными точками (чекпоинтами). Если игрока убивают, то он возвращается к началу этапа, но только если у него не кончились жизни. Если продолжить игру после Game Over, то игрока отправляют в самое начало уровня.

Номер этапа обозначает общее количество чекпоинтов, пройденных с начала игры. Последний этап имеет номер 18. Но если убить Дракулу, то игра начинается с самого начала в сложном режиме (Difficult Mode), а номера этапов продолжают увеличиваться до 19 и дальше. Если пройти Difficult Mode, то третий цикл игры не становится сложнее, но номера этапов по-прежнему продолжают увеличиваться.

4476a53c571dc062a4bc2287f5e02587.png


Каждый этап состоит из одной или более полосы фона с горизонтальным скроллингом, которые я называют «подэтапами». Если выйти за экран по ведущей вверх или вниз лестнице, то игра переходит от одного подэтапа к другому, а не скроллится вертикально.

7607515d8da99df95877d7eef8214f3f.png


Для отслеживания номера игрового цикла, номера этапа и номера подэтапа игра использует 3 байта. Игровой цикл в обычном режиме (Normal Mode) имеет значение 0; значения 1 и выше обозначают Difficult Mode. Вне зависимости от игрового цикла этапы всегда имеют нумерацию от 0 до 18. А подэтапы всегда имеют значение 0 или 1, потому что этапы никогда не содержат больше, чем 2 горизонтальных полосы. CastlevaniaBot отслеживает эти байты, чтобы знать, где он находится.

Для своего удобства я записал полосу фона каждого подэтапа в отдельный графический файл. Если мне необходимо было узнать точные координаты какого-то объекта, то я мог быстро их выяснить с помощью графического редактора. Файлы я записал с помощью встроенного в Nintaco инструмента Map Maker. Я включил его, а затем прошёл всю игру. Некоторые из получившихся изображений содержали соединённые вместе несколько подэтапов, которые было легко разбить на части.

Nintaco's Map Maker


Изучив изображения фонов, я понял, что единственными важными тайлами являются платформы и лестницы. Всё остальное можно считать пустым пространством. Лестницы бывают двух типов: ведущие вперёд или назад (их можно представить как левое и правое ребро равнобедренного треугольника). Иногда лестницы заканчиваются платформой, по которой может ходить игрок.

c6e6fa76bcd6d6c3457060763eb6f993.png


Существует всего 5 важных типов тайлов, и на каждом уровне они имеют разный внешний вид. Я скопировал и вставил их в отдельные графические файлы размером 16×16. Затем я написал программу, сравнивающую каждый тайл в каждом подэтапе с соответствующим набором изображений. Так я получил матрицы типов тайлов для всех подэтапов.

Матрицы можно было бы извлечь непосредственно из ROM, но я не смог найти документацию о том, как и где хранятся данные. Так как пространство ROM ограничено, данные уровней обычно представлены в сжатом виде, напоминающем формат векторной графики. В каждой игре используется собственный формат, поэтому я не посчитал необходимым проводить исследования, ведь у меня был Map Maker. Кроме того, мне всё равно были нужны графические изображения подэтапов. Если бы я не выполнил захват полос фонов, то мне бы пришлось писать программу, генерирующую изображения из матриц.


Создав матрицы, я захотел применить алгоритм поиска путей, а именно алгоритм Флойда-Уоршелла, который даёт на выходе таблицу, содержащую кратчайшие пути между каждой из пар вершин ориентированного графа со взвешенными рёбрами. Идея заключалась в том, чтобы предварительно вычислить таблицы и записать их в файлы, а затем загрузить их в процессе выполнения игры. Имея таблицы в памяти, бот может выполнять поиск и мгновенно узнавать кратчайший путь между любыми двумя тайлами платформ.

Граф состоит из рёбер и вершин. Если тайлы платформ являются вершинами, то рёбра — это только те операции, которые может выполнять Саймон, чтобы перемещаться от одной платформы к другой. Операции, выполняемые в пределах только одного тайла, запрещены. Например, на плоскости из тайлов Саймон может переместиться только на один тайл влево или один тайл вправо.

fe2fa303d199d5c1cb826da8003acd3a.png


Аналогично, если Саймон находится на лестнице, то он может перемещаться на один тайл в одном из двух возможных направлений.

a68f175026f789811cf3c7b032f76898.png


Это допустимые операции. Но такие действия, как движение от средней точки тайла до края тайла оказываются слишком дробными, потому что они не соответствуют переходу из одной вершины графа в другую.

Кроме ходьбы влево-вправо, спуска-поднимания по ступенькам Саймон может перепрыгивать на другой тайл. И он может прыгать влево или вправо, начиная с любого из 16 пикселей поверхности тайла. Чтобы уменьшить количество рёбер в графе (и размер таблицы поиска) я рассматривал только пять возможных точек отталкивания:

01237daabc08e79e6ccd6de83b6a1c0e.png


Добавив операцию «ничего не делать», я получил 15 операций. Все они достаточно просты для того, чтобы бот мог легко определить последовательность нажатия кнопок, необходимое для их выполнения во время выполнения игры.

Чтобы построить полный граф, я создал очень простую симуляцию физики мира Саймона. В этой симуляции все 15 операций выполняются начиная с каждого тайла платформ. Граф строится простым наблюдением за тем, где окажется Саймон. Если конкретнее, то при задании начальных координат и операции симуляция даёт на выходе конечные координаты. Если конечных координат нет, например, когда Саймон пытается пройти в стену или в яму, то программа возвращает, что операция недопустима и это ребро не является частью графа.

Для создания этой симуляции потребовалось тщательное изучение Саймона Бельмонта. В отличие от других классических платформеров, где игрок может ускоряться от ходьбы к бегу, Саймон при движении по горизонтали всегда перемещается ровно на 1 пиксель за кадр. Это справедливо для ходьбы, прыжков, взбирания по лестницам и даже при отбрасывании назад при атаках врагов.

Ограничение горизонтальной скорости очень упрощает распознавание барьеров. Игра проверяет, есть ли стена в одном пикселе перед персонажем, и при необходимости останавливает движение. Проверка выполняется ниже уровня головы, что позволяет голове проходить под низкими потолками без помех горизонтальному движению.

С вертикальным движением всё немного сложнее. Если Саймон сходит с края платформы, он вместо постепенного ускорения мгновенно падает вниз со скоростью 8 пикселей на кадр. Так как каждый тайл имеет высоту 16 пикселей (что кратно 8), распознавание земли упрощается. Одновременно игрок сохраняет горизонтальную скорость в 1 пиксель на кадр в направлении, полученном непосредственно перед падением.

Ширина спрайта Саймона — 16 пикселей, и тем не менее, он может нависать над блоком максимум на ±4 пикселя от его средней точки. Если сдвинуться чуть больше, то он упадёт ниже.

fd8b2a0a48035271fc78e7209d980dd5.png


Интересно, что если он сойдёт с платформы и упадёт ровно на 1 тайл со скоростью 1 горизонтальный и 8 вертикальных пикселей на кадр, то при приземлении не будет точно стоять на блоке. Одна из его ног будет оставаться внутри стены на глубину 2 пикселя.

da0a274588a45b4e448fbe46ef9e48ca.png


После этого он сможет выйти из стены, но не зайти в неё.

Во время прыжка Саймон не может менять его направление. После нажатия на кнопку A он совершает фиксированный путь по параболе.

041db3a4afa0f0f93e0533f0ee1dbd48.png


В верхней точке Саймон поднимается на 36 пикселей, что позволяет ему запрыгивать на платформы высотой 2 тайла. А вершина «параболы» на удивление плоская. Похоже, что он зависает в пространстве на 9 полных кадров, возможно для того, чтобы упростить удары хлыстом в воздухе. Если Саймону не на что приземлиться, то движение по параболе продолжается, пока он не вернётся на исходную высоту. После этого он начинает падать со скоростью 8 пикселей в кадр, то есть с той же постоянной скоростью падения с платформы.

Когда прямо над головой персонажа есть платформа, прыжки не допускаются.

e063580bac81a0ec963336e2886c0e2a.png


В противном случае он частично «впрыгивает» в тайлы платформ.

65156af41893e3c9008a4836d67efca1.png


Как говорилось раньше, если Саймон касается больше, чем верхушкой головы, он перестаёт двигаться по горизонтали. Это довольно раздражает в конце первого этапа уровня 4, когда игрок пытается выбраться из пещеры.

1c6d413312f17f67d6247f9a33000499.png


Лестницы позволяют свободно перемещаться по вертикали между платформами.

05c5e12fc0bd66681054052354e13a14.png


В одном из случаев Саймон поднимается по лестнице, проходя насквозь тайл платформы. Находясь на лестнице, он свободно может проходить через стену.

5228724383d3d908b5298c62dc73e015.png


На подвижных платформах уровня 4 игрок обычно пригибается под низко висящими сталактитами. Однако можно подняться и упереться в них. В такой ситуации игра ограничивает горизонтальное движение, быстро утягивая Саймона в водную ловушку.

febdcd85f8f47c08806d00343a74fc02.png


Симулятор физики, генерирующий ориентированные графы, считает Саймона прямоугольником. Движение этого прямоугольника ограничено описанными выше правилами перемещения. Чтобы бот не прыгал с места на место как кролик, рёбрам прыжков графа была назначена бОльшая стоимость, чем рёбрам ходьбы и движения по лестницам.

Таблица, сгенерированная алгоритмом поиска пути, содержит кратчайшее расстояние для каждого начального и конечного тайла (общую стоимость пути) и описание первого шага пути. В это описание входит первая операция, которую необходимо выполнить на этом пути, и тайл, на котором окажется персонаж после её выполнения. Весь путь можно воссоздать повторным поиском в таблице, каждый из которых даёт последующий шаг в направлении конечного тайла.

В некоторых подэтапах графы соединены не полностью. Например на уровне 4 единственным способом перебраться через пропасти являются платформы. В таких подэтапах таблица содержит способы перемещения по каждому из островов, но не между ними.

e0f098f72ffb3a91bb86d2cf5fa1921b.png


Внутри некоторых разрушаемых блоков содержатся скрытые предметы. И уничтожение блоков изменяет этот граф. Чтобы обойти эту проблему, к подэтапам был применён алгоритм поиска путей с разбиваемыми блоками и без них. Во время выполнения CastlevaniaBot наблюдает за состояниями разбиваемых блоков и использует соответствующую таблицу поиска.

acaac48a1c7f4acd0849f06dab3a9d0a.png



CastlevaniaBot интегрируется в Nintaco через его API. Он регистрирует реализацию FrameListener, чтобы получать в каждом кадре возврат. Так как эмулятор выполняется с частотой примерно 60 кадров в секунду, этот listener необходимо возвращать вовремя; длительные вычисления или простои замедлят или блокируют эмулятор. За короткий промежуток времени CastlevaniaBot считывает состояние игры, определяет свою основную цель, переключает стратегии, если цель изменилась и выполняет текущую стратегию.

CastlevaniaBot считывает текущее состояние Саймона Бельмонта непосредственно из ОЗУ процессора. Но я не могу определить, как представлены в памяти другие игровые объекты. Поэтому CastlevaniaBot выполняет считывание непосредственно из памяти атрибутов объектов (Object Attribute Memory, OAM) — области, хранящей список отображаемых спрайтов.

296b5846a26530aa657294d619e5ad63.png


Эта техника работает и в общем случае применима к другим играм, но имеет множество недостатков. Порядок спрайтов в OAM постоянно меняется. Если на экране одновременно есть несколько экземпляров одного типа врага, то единственный способ отслеживать их — определение их близости, сопоставление последних координат спрайта с координатами из предыдущего кадра.

46d23625808baf3abb91916e4f80baed.png


Некоторые игровые объекты состоят из повторяющихся спрайтов, например, костяные башни.

1b749adcb05680e5a22247c783d8bac2.png


Подвижные платформы состоят из одного спрайта, повторяющегося 4 раза.

7e0f85ea889b5e824a010d04e26f03d2.png


Для сортировки обоих этих случаев требуется дополнительная логика, которую реализовать совсем несложно. Апгрейды вторичного оружия и некоторые виды самого вторичного оружия используют одни и те же спрайты. Хуже того — на уровне 5 рыцари заимствуют спрайты вторичного оружия Саймона.

44ccd108ea9b25c442a5653d79f0c49e.png


К счастью, за исключением святой воды, CastlevaniaBot использует вторичное оружие только там, где апгрейды обычно недоступны, например в боях с боссами. И спрайт апгрейда святой воды отличается от спрайта, используемого при её бросании.

0141d2faf5e0168ae8d4046cdbfbe278.png


Некоторые игровые объекты при создании мигают, например хрустальные шары в конце боёв с боссами, косы Смерти и тело Дракулы. Мигающие спрайты появляются и исчезают из OAM, поэтому для отслеживания этих объектов требуется дополнительная логика.

b805f1f8bf89efb9c021b716a0d39e25.png


Также стоит заметить, что из-за аппаратных ограничений NES может отображать только 8 спрайтов на растровую строку. Так как приоритет спрайтов частично зависит от их индексов в OAM, порядок в каждом кадре произвольно перемешивается, чтобы избежать сдвига, делающего один спрайт невидимым постоянно. Несколько спрайтов мигает по очереди, постепенно изменяя свой приоритет. Это мигание не влияет на считывание спрайтов из OAM. Данные всё равно остаются там, но эмулируемое железо их не отображает. Это отличается от описанной выше ситуации с мигающими при создании спрайтами. Кроме того, в Nintaco есть опция, значительно снижающая аппаратное мигание.

6b9527fe61e614012f1fd50d1ad0625e.png


И наконец, небольшая часть состояния игры считывается из таблиц имён PPU — области памяти, содержащей все данные фонов. К нему относится проверка разрушаемых блоков и слежение за позициями «давилок» на уровне 2.

d35f3c378b284474c8d2b5386a78fb57.png



Стратегии — это машины состояний. CastlevaniaBot задаёт их как абстрактный класс с методами init и step. Метод init вызывается, когда CastlevaniaBot переключается на соответствующую стратегию, позволяя машине состояний выполнить сброс в исходное состояние. А метод step выполняет стратегию. Например метод step стратегии для людей-рыб проверяет, находится ли человек-рыба в пределах удара хлыстом, и если да, то бот ударяет хлыстом. В противном случае он проверяет, можно ли прыжком достичь расстояния удара, и если это так, то бот прыгает. И наконец, если бот стоит слишком близко к противнику, то отходит от него. Благодаря этим простым правилам CastlevaniaBot побеждает людей-рыб.

Чтобы максимально упростить написание стратегий, я создал библиотеку возможных выполняемых действий. Например вместо нажатия и отпускания кнопки A для прыжка бот просто вызывает из библиотеки метод прыжка. А перед этим он запрашивает библиотеку, находится ли Саймон на платформе и может ли прыгать.

Приближение и удаление от цели — это стандартные действия, выполняемые при помощи операций из таблицы, созданной алгоритмом поиска путей. Например, стратегия свечей использует библиотечный метод routeAndFace, который не только направляет Саймона в какие-то указанные координаты, но и поворачивает его влево или вправо после попадания в них. Кроме того, в зависимости от высоты расположения свечей стратегия выполняет перед ударом хлыстом прыжок или приседание. Выпавший из свечей предмет создастся в воздухе и упадёт или медленно опустится на землю. Стратегия поднимания предметов направляет Саймона к ближайшему тайлу непосредственно под ним ещё до того, как предмет коснётся земли.

Чтобы отодвигаться от врагов, библиотеке нужно знать, как двигаться влево или вправо, не падая в ямы. В общем случае это реализуется поиском путей к левому или правому краям подэтапа. Но в некоторых областях кратчайший путь к левому краю изначально требует двигаться вправо, и наоборот. Когда появились подобные проблемы, я добавил логику специально для подэтапов, направляющую Саймона к левому и правому краям текущей платформы.


В этом разделе я подробно опишу используемые CastlevaniaBot стратегии, прокомментировав всё прохождение.

Игра начинается во дворе перед замком. Объекты в кадре проранжированы для выбора основной цели, которой в данном случае является колонна с пламенем. Стратегия работы с колоннами приказывает боту приблизиться к колонне и использовать хлыст, когда он будет в пределах досягаемости.

14c6477507cee1d822e6cadbcafbda5a.png


После удара хлыстом и уничтожения колонны создаётся предмет. Приоритет всех предметов выше, чем у колонн с пламенем. Следовательно, CastlevaniaBot реагирует на это, используя стратегию сбора появившегося предмета, прежде чем переходить к последующим колоннам.

При ранжировании объектов CastlevaniaBot всегда учитывает долговременную цель — прохождение уровня до того, пока кончится время. При создании списка потенциальных целей в него всегда добавляется дверь на следующий уровень. Двери назначается низкий приоритет, но после уничтожения всех колонн и сбора всех предметов она становится приоритетной. Только за одним исключением: CastlevaniaBot знает, как раскрывать все скрытые сокровища, и перепрыгивает через вход в замок, чтобы создать мигающий мешок с деньгами.

2007f5eaa4405bd611bd8711017fbe24.png


После входа в замок CastlevaniaBot видит призраков и свечи. Свечи и создаваемые предметы имеют больший приоритет, пока призраки не подберутся ближе.

8a79ab84345e6a16a9231c5a7f45ceae.png


Часто кажется, что хлыст уничтожает врагов, не касаясь их. Я нашёл в ROM игры таблицу прямоугольников коллизий, поэтому CastlevaniaBot точно знает, когда что-то находится в пределах действия хлыста. А поскольку прямоугольники коллизий часто слегка выдаются за границы спрайтов, хлыст может ударять «невидимой частью».

В большинстве случаев при уничтожении свечей CastlevaniaBot прыгает вертикально, а не вперёд. Это тактика избегания риска. Так как находясь в воздухе, изменить направление движения невозможно, толпа приближающихся врагов может усложнить безопасное приземление или сделать его невозможным. Кроме того, при вертикальном прыжке CastlevaniaBot остаётся на том же блоке; ему не нужно проверять, приведёт ли прыжок к падению на нижнюю платформу или дно ямы.

Когда не остаётся ни уничтожаемых свечей, ни собираемых предметов, CastlevaniaBot начинает преследовать призраков. Но «стратегия пантеры» приказывает ему стоять на месте, ожидая пока враг не попадёт в пределы удара хлыста.

4349a0bd58a7078c6b0d1951845bfea2.png


Кроме того, что свечи служат источниками сердец и других предметов, они направляют игрока по пути через этап. Например на показанным ниже изображении свечи в верхнем правом углу «приглашают» игрока подняться по лестнице. Однако чтобы подняться вверх по ступеням, игроку изначально нужно пройти влево, из-за чего свечи пропадают с экрана. Так как решения CastlevaniaBot основаны на расстановке приоритетов видимых на экране предметов, эта ситуация может привести к бесконечному циклу, где бот попеременно выбирает кратчайший путь к свечам (влево) или кратчайший путь к двери выхода (вправо).

309d1a8e7f1f5a39a8b34411108b94b8.png


Подобные проблемы можно решить несколькими способами. Например, CastlevaniaBot можно оснастить запоминанием объектов, пониманием того, что объекты продолжают существовать, даже если их не видно. Но для этого всё равно изначально их нужно увидеть. Если бы свечи были ещё правее, то после их обнаружения нужно было бы возвращаться назад ещё дальше.

С учётом этого я добавил стимулы, подталкивающие CastlevaniaBot к исследованию областей этапа, которые он обычно бы игнорировал. Эти стимулы работают аналогично выходным дверям, привлекая CastlevaniaBot туда, куда ему нужно идти. Кроме того, я добавил правила, заставляющие его игнорировать свечи и предметы, находящиеся слишком далеко (по проходимому расстоянию, а не по прямой линии).

После поднятия по лестнице и уничтожения свечей создаётся святая вода — самое ценное из всего вторичного оружия. CastlevaniaBot выбирает его, обменивая на него кинжал.

4c557500adad3a899c43ea80b983d6fb.png


Как и в случае с денежным мешком в начале игры, CastlevaniaBot знает обо всех разрушаемых блоках со скрытыми предметами, и для разрушения блоков хлыстом он использует стратегию, похожую на стратегию для свечей.

1eb007d4384996127451979738a3c6ea.png


При дальнейшем движении CastlevaniaBot вы заметите, что он начинает использовать для уничтожения призраков и свечей святую воду. Это может показаться излишним, но служит важной цели. Castlevania вознаграждает игроков за использование вторичного оружия с двойным и тройным воздействием. Другими словами, CastlevaniaBot гриндит апгрейды вторичного оружия.

6a7186be0588f46012a241cc9745d586.png


После нажатия кнопки B существует задержка в 16 кадров до удара хлыста. Хлыст остаётся длинным в течение дополнительных 10 кадров, но действует только в первом из этих десяти. CastlevaniaBot использует этот факт для улучшения прицеливания. В частности, он отслеживает скорости всех объектов в кадре, предполагая, что основная цель будет продолжать двигаться по линейной траектории. Затем он нажимает кнопку B за 16 кадров до того, как цель должна встретиться с ударом хлыста. Враг всегда может изменить направление в течение этих 16 кадров, но обычно эта простая эвристика работает.

Однако после прохождения через дверь CastlevaniaBot встречает красных летучих мышей. Как и головы медуз, красные летучие мыши летают по экрану, пролетая по синусоиде сквозь платформы и лестницы. Чтобы предсказать, где будет красная летучая мышь спустя 16 кадров, я записал движение одной из них в таблицу. Во время выполнения игры CastlevaniaBot отслеживает летучих мышей и ждёт пиков или минимумов траектории, где вертикальная скорость меняет свой знак. Тогда он способен сопоставить координаты со значениями в заранее созданной таблице. Это позволяет соответствующей стратегии ударить красную летучую мышь хлыстом, наклониться или подпрыгнуть над ней.

При скоростном прохождении (спидранах) Castlevania игроки выполняют маневры, делающие игру как можно более детерминированной. Например, они обнаружили, что если в конце уровня 1 разрушить блок и подобрать апгрейд оружия в определённой временной последовательности, то босс будет вести себя в соответствии с желательным паттерном, позволяющим быстро его победить.

Какими бы опытными ни были люди, они не могут полностью приручить генератор случайных чисел. Тем не менее, поскольку CastlevaniaBot может контролировать нажатия кнопок с покадровой точностью, он может довести концепцию скоростного прохождения до её предела, каждый раз проходя игру абсолютно идентичным образом. Если бы он так делал, это бы ничуть не лучше прохождения TAS. Поэтому CastlevaniaBot произвольно добавляет с помощью внешнего генератора случайных чисел в свои действия ошибки и задержки, чтобы избежать детерминированного геймплея. Например, когда он ударяет хлыстом находящуюся высоко свечу, то намеренно добавляет в прыжок и удар задержку на случайное количество кадров. Подобные крошечные изменения значительно влияют на прохождение игры.

Хотя CastlevaniaBot и стремится избежать детерминированности, он всё же позаимствовал у спидраннинга одну концепцию: damage boosts. В 50% случаев CastlevaniaBot полностью избегает прохождение подземелья людей-рыб, прыгая назад на красную летучую мышь, которая отбрасывает его на недостижимую иным способом платформу.

3757331edfa439d3ed9eb1ef3be8443e.png


В другой половине случаев CastlevaniaBot выбирает сражаться с людями-рыбами. Их огненные шары имеют высокий приоритет и CastlevaniaBot ударяет их хлыстом, уклоняется или подпрыгивает в зависимости от ситуации.

6e4f51077f42c471bc0484e64aaa14b6.png


Из-за непредсказуемости создания врагов и их огненных шаров большинство игроков обычно пробегает часть с людьми-рыбами. Но CastlevaniaBot тратит на них время, собирает все сердца и даже скрытое сокровище в самом правом краю. Бот точно знает, какие предметы содержатся в каждой свече, и поскольку он предпочитает пользоваться святой водой, то пропускает свечи, в которых скрыты хронометры. Однако если остаться в этой части и сражаться с людьми-рыбами, то иногда можно неизбежно подобрать хронометр.

Этапы часто завершаются крестами, уничтожающими всех врагов на экране. На случай, если вам любопытно, скажу — кресты не могут убивать боссов в конце уровня. На самом деле кресты иногда создаются даже во время боёв с боссами. Например, в битве с Медузой при уничтожении змей иногда появляются кресты. Но эти кресты могут убивать только других змей.

c3e328e311c90509ffc2450e4b991436.png


После прохождения через дверь CastlevaniaBot снова встречается с призраками. На показанном ниже скриншоте он хочет спрыгнуть на нижнюю платформу, но его движению препятствуют призраки. Когда созданная алгоритмом поиска путей таблица приказывает CastlevaniaBot прыгать, он изучает пространство вокруг целевой платформы. Если окажется, что он приземлится на врагов, то прыжок не выполняется. В данном случае этот механизм заставляет CastlevaniaBot дождаться, пока призраки освободят ему место.

c3b2ef81726485f94f1f391efa1ab2e6.png


Это правило «проверяй наличие врагов перед прыжком» иллюстрирует один из принципов структуры CastlevaniaBot: он должен управляться простыми, как можно более обобщёнными правилами. В некоторых частях игры необходимо использовать одноразовые стратегии, решающие очень конкретные задачи. Но в бОльшей части игры действия бота управляются многоразовыми эвристиками. Я не писал команду, заставляющую его остановиться в этой конкретной точке и ждать, пока призраки уйдут с пути. Это поведение возникло как результат эвристик.

CastlevaniaBot завершает уровень, убив Phantom Bat тройной святой водой. Но его стратегия атаки определяется его вторичным оружием. На самом деле, если бы у него не было вторичного оружия, он был готов к убийству босса с помощью одного кнута. Особо интересным случаем является убийство Phantom Bat с помощью топора. Аналогично случаю с красной летучей мышью, я записал в таблицу движение топора по параболе. Во время выполнения игры CastlevaniaBot выполняет смещение таблицы, сопоставляя её с каждым из тайлов пола. Это позволяет вычислить оптимальную точку для убийства босса топором.

d0c4802de26d9629ca4376a67513106f.png


Непосредственно перед подбиранием хрустального шара CastlevaniaBot бросает в разные стороны вторичное оружие, зная, что оно замрёт в воздухе после того, как он коснётся шара. Затем он случайным образом многократно прыгает и ударяет хлыстом, надеясь замереть в странной позе.

f9083bd561d92261d581128157bdf205.png


В начале уровне 2 CastlevaniaBot избегает первых свечей, в которых спрятан бумеранг. Стратегия работы с рыцарем-копейщиком похода на стратегии призраков или людей-рыб, однако это первый враг, для убийства которого нужны два удара. Чёрная летучая мышь дремлет, пока к ней не приблизишься, после чего она взлетает и движется достаточно линейно, чтобы её можно было убить хлыстом с помощью упомянутой выше простой эвристики.

a3ed2301ee4a6689ef64648439eb1939.png


Разрушив кирпичи стены, в 50% случаев CastlevaniaBot, похоже, уходит из комнаты, не подняв корону. Но так ли это? На самом деле в таких случаях он подбирает корону, воспользовавшись багом игры. Так как конец верхней лестницы совпадает по горизонтали с расположением короны, игрок при прохождении вверх и за экран на мгновение оказывается внизу. Если прислушаться к звуку или посмотреть на очки, то можно увидеть, что бот на самом деле берёт корону.

348459ef8dc72330b2d1bf99c63eb07d.png


На уровне 2 враги не атакуют игрока, когда он находится на подвижной платформе. CastlevaniaBot просто должен подождать, пока платформа подъедет, зайти на платформу, дождаться, пока она доедет до другой стороны, а зат

© Habrahabr.ru