Платформа NyxtaleЯзык Nyx
Случайность и детерминизм
rand(), Mulberry32, rng_seed: почему превью и игрок обязаны прийти в одну концовку.
Случайность в истории воспроизводимая: одно и то же прохождение всегда даёт один и тот же бросок.
rand
@set roll = rand(1, 6)
@if roll >= 5 -> steady
@if roll == 1 -> disaster
narrator: Рука дрогнула, но фонарь удержался.
rand(a, b) даёт целое от a до b включительно. Границы — константы или выражения; если верхняя
оказалась меньше нижней, движок вернёт нижнюю, а валидатор скажет об этом заранее.
Зерно
У прохождения есть зерно (rng_seed) — число, с которого начинается случайность. Оно фиксируется на
старте, лежит в состоянии и попадает в сохранение вместе с флагами; каждый вызов rand двигает
генератор на шаг вперёд.
Отсюда:
- Сохранение даёт ту же историю. Загрузился перед броском — бросок повторится тот же. Переиграть до удачного через перезагрузку нельзя.
- Превью совпадает с прохождением читателя. При одном зерне ветки те же.
- Отчёт проверки воспроизводим. Бот-обход, нашедший недостижимую концовку, найдёт её и завтра.
Один генератор на обоих движках
Генератор — Mulberry32, он записан в формате. Превью на TypeScript и приложение на GDScript выполняют одни и те же шаги над 32-битным беззнаковым числом и выдают бит в бит одинаковую последовательность; совпадение проверяется тестами соответствия на обеих сторонах.
Стандартный random языка здесь не используется: у каждого языка он свой, и два стандартных
генератора не совпадают. По той же причине арифметика
целочисленная.
Как этим пользоваться
Бросок один раз, читается много. Запишите результат во флаг и читайте флаг: два rand в двух
условиях — это два разных броска, даже написанные рядом.
@set roll = 0
@set roll = rand(1, 20)
@if roll >= 15 -> success
@if roll <= 3 -> fumble
-> partial
Случайность лучше работает на оттенке, чем на развилке. Бросок, решающий судьбу героя, читается как несправедливость. Бросок, решающий, какой из трёх шорохов он услышит, делает вторую читку другой.
Проверка навыка — это rand плюс флаг персонажа. Тогда на бросок влияет то, что читатель успел
накопить, а не только случайность (подробно — прогрессия):
@set nerve = 0
@set roll = 0
@set roll = rand(1, 20) + nerve
@if roll >= 14 -> steady_hand
-> shaking
Что дальше
Бот-обход стоит на том же детерминизме: он ходит по всем путям и приходит к одному отчёту дважды подряд.