Почитываю иногда темы оффлайн-турниров на ХП, в основном в целях улучшения знания матчасти, ребята иногда весьма неочевидные вещи находят.
В онлайне нередки случаи дисконнекта, и иногда при повторе передачи хода комп ходит иначе, чем первоначально. Нарезка постов из дискуссии оффлайнщиков по этому вопросу:
handbookhmm.ru/1-moral-luck-ppb
Хотя статья написана по Героям 1, насколько я знаю, ГПСЧ в 2 отличается только переинициализацией в начале боя в зависимости от армий, а в 3 ещё зависимость от анимации в бою прикрутили, в остальном - то же самое. Как можно догадаться из постов выше, я допускаю, что есть ещё какие-то отличия, но...
R — это псевдослучайное число, которое используется для нужд алгоритмов, отвечающих за «случайные» события в игре (в том числе и за выпадение морали и удачи).
Текущее значение R - это четырёхбайтное значение (двойное слово, DWORD), которое хранится в оперативной памяти на момент начала проверки.
Данное число порождает последовательность псевдослучайных чисел по известному алгоритму, поэтому достаточно знать текущее (или начальное) значение R, чтобы можно было вычислить любое другое.
При самой проверке происходит генерация нового псевдослучайного числа, которое и используется для определения результата проверки. Схематично это выглядит так: R[0] или Seed -> R[1] -> R[2] -> ... Значение Seed инициализирует ГПСЧ (может зависеть, например, от текущего времени; генерируется во время запуска игры, поэтому перезапуск во многих случаях оказывается так полезен). Каждое игровое событие, требующее броска игральной кости, берёт в качестве Seed ( "семени" ) значение, оставшееся от предыдущей генерации, однако использует новое, полученное с помощью специального алгоритма из предыдущего. Исключением* является левел-ап, когда в качестве предыдущего случайного числа выступает значение функции, аргументами которой являются уровень героя и номер его дерева прокачки.
Уровень повышается на всех машинах одинаково, так что если сейв загружен непосредственно после получения уровня, то различий никаких быть не должно. Однако если игрок зашёл в город и открыл окно форта или посмотрел на статы какого-нибудь отряда в его армии, то значение R изменится (в цикле анимации существ).
Ещё некоторые события не используют предыдущее случайное значение в качестве "затравки". Например, Seed для каждого боя вычисляется отдельно и не является случайным числом, т.к. разбиение нейтралов на стеки, появление грейженого стека и генерация препятствий на поле боя также не являются случайными (зависят от координат клетки, на которой происходит битва). Кстати, именно поэтому можно использовать таймер/секундомер для ловли морали и удачи.
Проверить, использует ли эта функция глобальный ГПСЧ, можно, например, следующим образом. Перед каждой загрузкой сейва, рассматриваемого в данной теме, загружайте сперва другой, заранее подготовленный сейв (dummy), где какой-нибудь герой берёт сундучок на опыт и повышает свой уровень. Теперь загрузите собственно турнирный сейв и передайте ход. Запишите результат и повторите описанную процедуру несколько раз. Между загрузкой сейва dummy и передачей хода в турнирном сейве не выходите из игры, не открывайте окно форта или окно с информацией об отряде, не изменяйте игровые настройки громкости. Теперь попробуйте вместо сейва dummy загружать какой-либо другой сейв dummy1, в котором уровень повышает уже другой герой. Рано или поздно Вы должны будете подобрать такой сейв dummyN, после загрузки которого герой компа будет нападать всегда, причём это сможет повторить кто угодно и на какой угодно машине.
А сколько различных уникальных чисел для seed можно сгенерить через level-up?
Хороший вопрос Нужно посчитать, сколько различных значений может принимать следующая функция:
R(Level, TreeNo) = (343FDh * Level + 26497h * TreeNo + 259DFh) * 343FDh + 269EC3h
Причём здесь возможно переполнение, так что необходимо ещё добавить в конец and 0FFFFFFFFh.
Порядок определить совсем не сложно. Поскольку TreeNo, как я понял из описания, фиксировано для каждого конкретного героя, то функция может принимать всего 75 значений (именно столько раз можно корректно повысить уровень героя, согласно ФизМиГу).
75 нужно умножить на 255, т.к. на "левом" сейве можно выбрать абсолютно любого героя для получения уровня. Итого, 75 * 255 = 19125.
Добавлено через 6 минут
Для онлайна здесь вижу сложность, что при загрузке сейва как минимум 1 раз генерируется новое случайное число - при выборе монстра, анимация которого будет сопровождать загрузку. Хз сбивает ли это настройки ГПСЧ и хз перегенерируется ли оно в процессе анимации (как у юнитов на карте).
Еще можно попросить Бараторча реализовать выбор seed-а для хода компа независимым от R, как для левелапа. Например, привязать его к номеру игрового дня, тогда повтор хода (с точным сохранением армии) будет давать однозначность хода компа.