Перейти к основному содержимому

20.3. Как сделать шанс на выпадение определённой вещи?

В: Как сделать шанс на выпадение определённой вещи? Например 25 процентный шанс на то, что в сундуке окажется золотая чаша?

О:

В самом простом виде, когда мы имеем дело с одним конкретным предметом и приблизительно знаем шансы его выпадения, можно сделать весьма простой алгоритм:

! задаём шанс выпадения предмета (якобы в процентах) — предел от 1 до ...
chance=25
! теперь выбрасываем случайное число от 1 до 100
bones=rand(1,100)
! проверяем попало ли выпавшее число в заданный нами предел
if bones<=chance:
! если выпавшее число меньше либо равно заданному
! получаем предмет
addobj "Золотая чаша"
"Вы нашли в сундуке золотую чашу."
else
! в противном случае ничего не получаем
"Вы ничего не нашли в сундуке."
end

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

Золотая чаша - 25%
Кувшин - 25%
Вострый меч — 50%
Убольшающее зелье — 25%

Если сложить все процентные значения в этом случае, мы получаем 125% на все предметы, но такого не может быть.

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

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

Вострый меч — 50
Убольшающее зелье — 25

Теперь мы знаем, что это не проценты, а коэффициенты соотношений шанса выпадения. Делим 50 на 25, и понимаем, что Вострый меч должен выпадать вдвое чаще, чем Убольшающее зелье. При условии, что хоть один предмет должен выпасть обязательно, составляем следующий алгоритм:

! максимальное значение случайного числа (складывается из коэффициентов)
bones_max = 50+25
! определяем отрезок для меча (от 1 до 50)
chance_sword = 50
! определяем отрезок для зелья (от 51 до 75)
chance_potion = 75
! выбрасываем случайное число от 1 до bones_max
bones = rand(1, bones_max)
! теперь используя сложное условие проверяем отрезки
if bones <= chance_sword:
! если мы уложились в отрезок для меча
! получаем меч
addobj "Вострый меч"
elseif bones <= chance_potion:
! иначе если мы уложились в отрезок для зелья
! получаем зелье
addobj "Убольшающее зелье"
end

Теперь можно составить алгоритм для случая с четырьмя предметами. Он не будет сильно отличаться от предыдущего:

! максимальное значение числа
bones_max=50+25+25+25
! отрезок меча (1-50)
chance_sword=50
! отрезок зелья (51-75)
chance_potion=75
! отрезок кувшина (76-100)
chance_jug=100
! отрезок чаши (101-125)
chance_cup=125
! выбрасываем число
bones=rand(1,bones_max)
! получаем выпавший предмет
if bones<=chance_sword:
addobj "Вострый меч"
elseif bones<=chance_potion:
addobj "Убольшающее зелье"
elseif bones<=chance_jug:
addobj "Кувшин"
elseif bones<=chance_cup:
addobj "Золотая чаша"
end

Что если мы хотим, чтобы в принципе предмет мог выпасть с 50% вероятностью, но между собой они делили шансы выпадения по-прежнему? Просто увеличиваем максимальное значение числа вдвое:

! максимальное значение числа
bones_max=(50+25+25+25)*2
! отрезок меча (1-50)
chance_sword=50
! отрезок зелья (51-75)
chance_potion=75
! отрезок кувшина (76-100)
chance_jug=100
! отрезок чаши (101-125)
chance_cup=125
! выбрасываем число
bones=rand(1,bones_max)
! получаем выпавший предмет
if bones<=chance_sword:
addobj "Вострый меч"
elseif bones<=chance_potion:
addobj "Убольшающее зелье"
elseif bones<=chance_jug:
addobj "Кувшин"
elseif bones<=chance_cup:
addobj "Золотая чаша"
else
! или ничего не получаем
"Вы ничего не нашли"
end

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

В QSP версий 5.9.0 и выше появилась возможность сделать плавное распределение выпадения тех или иных значений. За это отвечает третий параметр функции RAND, который называется мода.

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

$item[0] = "Золотая чаша"
$item[1] = "Убольшающее зель"
$item[2] = "Кувшин"
$item[3] = "Вострый меч"
$item[4] = ""

$object = $item[rand(0,4,4)]
if $object: addobj $object

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

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