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

Допустим, вы хотите, чтобы персонаж «повезло». Вместо того, чтобы бросить один d20, вы бросаете два и получаете наивысший результат. Теперь у вас в два раза больше шансов выбросить 20! Но у вас в 20 раз меньше шансов выбросить 1 (единственный способ бросить 1 сейчас - это сделать так, чтобы оба кубика бросили 1, что составляет 1 шанс из 400!). Этот персонаж, безусловно, очень удачлив, и он почти никогда не проиграет. что он пытается сделать.

Но как получить что-то среднее? Может быть, вы хотите, чтобы персонаж был немного удачлив, но не настолько удачлив, чтобы он, по сути, никогда не выбрасывал 1. Вы хотите, чтобы шансы сложились в его пользу, но все же хотите, чтобы у него были разумные шансы на плохой бросок. Это был вопрос игрового дизайна, который мне нужно было решить на днях. Мне было любопытно, а что, если бы вы могли бросить «1 с половиной кубика» вместо 2? Что это будет значить и каковы будут последствия?

Итак, давайте проверим существующую математическую литературу по этой теме. Вот статья о том, как подбросить половину монеты. Основная идея здесь заключается в том, что подбрасывание 2 половинок соответствует результатам, которые вы ожидаете от подбрасывания одной монеты (50% шанс выпадения 1 решки и 50% вероятности 1 решки), аналогично тому, как подбрасывать 2 монеты дважды. то же самое, что подбрасывать 4 монеты при подсчете количества орлов и решек в результатах. Так как же выглядит полмонета? Что ж, это бесконечно односторонняя монета, где вероятность попадания в определенные стороны может иметь отрицательные вероятности. Что… эээ…

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

Моя первоначальная мысль заключалась в том, что «1.5d20» может означать 50% шанс выпадения 1d20, 50% шанс выпадения 2d20. По сути, бросьте монетку, чтобы определить, повезло вам в этом броске или нет. (В некоторых играх, таких как DnD, такие дополнительные броски называются «Преимущество»). И это в значительной степени именно то, что я хочу. Это очень хорошо обобщается на любые дробные броски кубиков, 1.1d20, 7.8d20 и т. Д. Черт возьми, вы даже можете расширить его до отрицательных бросков, если просто примите «-2d20» как значение броска 2 и возьмете наименьшее.

Но я не был полностью удовлетворен здесь. Что-то в этом было похоже на то, что это, вероятно, был не «математически правильный» способ сделать это. Кроме того, реализация этого в коде означала цикл for, в котором вы берете целую кучу случайных чисел и возвращаете максимальное значение. Что, если вы хотите делать безумные числа, например 1000000d20? Получить максимум миллиона чисел намного дороже, чем получить максимум пары чисел. Моя мотивация здесь заключалась не столько в производительности, сколько в том, чтобы просто найти формулу закрытой формы, которая дает такое же распределение, как бросание нескольких кубиков, в надежде, что она отображает дробные значения более математически удовлетворительным образом.

Итак, я решил попытаться понять это. Краткий фрагмент соответствующей информации здесь: чтобы вычислить случайные целые числа в диапазоне с помощью кода, вы сначала берете случайное действительное число от 0,0 до 1,0 (исключая, вы можете получить ровно 0, но вы не можете получить ровно 1), а затем сопоставляете это число в диапазон, который вы хотите (чтобы сопоставить его с диапазоном от 1 до 20, вы умножаете его на 20 и добавляете 1, теперь у вас есть число от 1 до 21 (но не ровно 21), затем вы отрезаете десятичные точки, чтобы получить целое число от 1 до 20)

Теперь у вас должна быть возможность сначала запустить это начальное случайное число (от 0 до 1) через функцию (я назову это «функцией модификатора случайного результата»), а затем, когда вы сопоставите его с «1–20», вы получите с разными вероятностями для каждого числа. В какой диапазон вы его сопоставляете, на самом деле не имеет большого значения здесь, поскольку форма распределений будет выглядеть одинаково, независимо от того, как вы ее сопоставите (только короче), поэтому я просто собираюсь работать с этими непрерывными диапазонами от 0 до 1 вместо этого пока.

Давайте быстро посмотрим, как эти распределения выглядят в виде гистограмм. Это быстрое приложение, которое я написал, которое имитирует множество непрерывных бросков костей, а затем отображает результаты в виде гистограммы. Никакие единицы не показаны, потому что, опять же, на самом деле важна только форма (но левая сторона равна 0, правая сторона - 1, а площадь под кривой всегда равна 1 (вероятности, йо!)). Обратите внимание, что когда вы бросаете один кубик, кривая представляет собой ровную линию (равные вероятности для каждого варианта). Когда вы бросаете 2 кубика, вы получаете увеличивающуюся линию (более высокий шанс выпадения большего числа).

Теперь вы должны заметить, что каждый раз это просто простой многочлен! На 1 кубике график выглядит как x⁰, на 2 кубиках - как x¹, на 3 кубиках - как x² и так далее. Простая экспоненциальная формула, которая имеет естественное расширение до дробных чисел! Так что давайте просто попробуем. Давайте просто возьмем наше исходное случайное число и возведем его в степень n-1 (где n - количество кубиков, которые нужно бросить). Эм-м-м…

Да, это не совсем сработало, и по этой причине функция, представляющая нужное нам распределение, не отображается на функцию, которую мы используем для непосредственного изменения исходного случайного числа. По-видимому, здесь есть некоторая взаимосвязь расчетов, которая имеет смысл, поскольку площадь под кривой здесь является релевантным значением. Также существует обратная зависимость, которая имеет смысл, поскольку, если нашей функции нравится подтягивать значения к 0 (например, x² в этом диапазоне), это означает, что более низкие значения будут более распространенными. Вероятно, существует какой-нибудь строгий математический способ преобразовать желаемую функцию распределения в функцию-модификатор случайного результата, но вместо этого я просто решил попробовать и сделать ошибку. Если вместо этого мы возведем его в степень 1 / n, мы получим здесь то, что хотим.

И теперь мы можем протестировать эту функцию с дробными кубиками!

Основная проблема здесь в том, что бросок 1,1 кубика действительно снижает шансы получить низкие броски, что намного больше, чем можно было бы предположить «10% удачи». И мне кажется странным, что функция между плоской линией и прямой линией была бы какой-то странной кривой, похожей на sqrt-ish. Хотя я думаю, что это, вероятно, лучший результат, который мы можем получить для «математически точных» частичных бросков кубиков, я также думаю, что он не дает того, чего вы хотите с точки зрения дизайна игры.

Итак, давай вернемся и проверим мою первоначальную мысль о частичных бросках костей. 1,5 броска кубика означают 50% шанс выпадения одного кубика и 50% шанс выпадения двух. Распределения вероятностей для этого метода выглядят так:

О, на самом деле это выглядит намного лучше. Промежуточные значения на самом деле выглядят как функции, которые оказываются между целочисленными значениями. Мы также можем полузакрыть его, учитывая, что степенная версия согласуется с этим, когда количество кубиков является целым числом, поэтому мы все еще можем использовать это, как только мы узнаем, сколько «целых бросков» мы хотим. Один бросок, чтобы определить, сколько бросков использовать, затем один бросок, пропущенный через функцию мощности, чтобы получить результат. Сладкий! Этот метод также довольно хорошо обобщается и на другие формы бросков кубиков (например, суммирование результатов бросков нескольких кубиков).

Оказывается, это еще одна из тех вещей, где «математически правильный» способ сделать что-то - не совсем правильный способ сделать что-то в контексте игрового дизайна. Возможно, я зря потратил свое время на изучение этого материала. Но по крайней мере теперь я знаю!

Итак, чтобы ответить на вопрос, поднятый заголовком этой статьи, как вы бросаете половину кубика? Простой. Подбросьте монету, если выпала голова. Если решка, не надо.