Безбилетный проезд по пространству состояний
Когда разработчик сооружает компьютерную игру, в его планы обычно входит реализация некоторого POMDP - марковского процесса принятия решений, пусть даже с частичной наблюдаемостью.
Но на наше счастье, реализацией этого POMDP занималась всего лишь большая команда обезьян, а не сверхинтеллект, и поэтому она не идеальна, а значит, можно попасть в состояния игры, которые заранее не были задуманы.
В качестве примера посмотрим на мою любимую GTA: San Andreas. В ней совпало 2 фактора - она очень большая и разнообразная, но при этом в начале нулевых, когда её создавали, игровые движки ещё не были настолько продвинутые и безопасные.
Состояние игры - это просто большой массив байтов. Если всё идёт по плану, то у каждого байта есть конкретное назначение, скрипты читают и пишут только то, что им положено, и только тогда, когда положено. Короче, всё держится на честном слове. Если перемещаться по заранее заданным маршрутам, состояние игры будет оставаться "корректным".
Но... зачем себя ограничивать? Зачем оставаться в клетке, когда есть целых 2^N состояний, открытых для исследования, а не жалкие крохи, предусмотренные разрабами?
Рассмотрим на примере, как происходит открытие портала в ад. Есть такая глобальная переменная $ON_MISSION - бинарный флаг, Если она равна 0, то герой находится вне миссии, и ему можно начать миссию. Если она равна 1, то он на миссии, и новую начинать нельзя. Что может пойти не так?
В игре есть мобильный телефон. Когда вам звонят, $ON_MISSION переключается на 1, а когда звонок заканчивается, она переключается на 0 - звонок считается чем-то вроде миссии. Но эта логика реализована не совсем безопасно. Если вам в один и тот же кадр поступает звонок и вы начинаете дополнительную миссию, то происходят параллельные манипуляции с этой переменной двумя разными скриптами.
В случае определённых действий игрока, можно добиться, чтобы этот звонок определённым образом застрял в памяти, а после выхода из дополнительной миссии в $ON_MISSION будет снова записан 0. Получается, что звонок в памяти есть тогда, когда его быть не должно.
Теперь, если вы начнёте какую-нибудь миссию, переменная $ON_MISSION станет равна 1, а затем активируете тот самый отложенный звонок, то в момент завершения он перепишет $ON_MISSION обратно в 0, хотя вообще-то миссия активирована.
Корректность состояния мира - вещь крайне хрупкая. Разбив её, перемещение по этому пространству состояний перестаёт поддаваться человеческой логике. Возможность перемещаться по миру с $ON_MISSION = 0, будучи на миссии, открывает портал в параллельные вселенные. Буквально.
Продолжая ломать состояние всё дальше и дальше, можно триггернуть так называемую команду "Goto 0", которая начнёт новую игру внутри уже существующей игры. Прям начиная с первой заставки. Игра начнёт перезаписывать уже текущее состояние поверх существующего, но не сбросит его полностью. В игре начинает существовать 2 разных главных героя, которыми мы управляем одновременно. Вновь появляется возможность пройти первую миссию, хотя можно зайти и открытую в "прошлой" игре.
Как можно предположить, игра в этот момент уходит в полный разнос. Как насчёт нового языка программирования CJ++?
Игроки научились исполнять произвольный игровой скрипт, записанный в локации в памяти, в которую записывается ваша последняя ставка в определённом казино на определённом колесе фортуны.
Теперь скоростное прохождение игры за ~40 минут заключается не в проскакивании всех сюжетных миссий, а в сетапе этого глюка с последующей записью команды "прыгни в конец игры" с помощью программирования в казино, и затем запуска исполнения этого самого кода. Если кому интересно, вот есть видео с более подробным объяснением.
Есть в этих блужданиях по POMDP какая-то необъяснимая романтика...
@knowledge_accumulator
Post #342
4.19K
- ❤ 28
- 🔥 15
- 👍 6
- 🤯 3
- 🤡 1
- 🎃 1