Я нахожусь в процессе создания новой простой игры для мобильных устройств, и я потратил несколько дней на следующую часть.
Для простоты, скажем, у меня есть два бойца. Единственный их атрибут - это атака и защита. При первой атаке важно только нападение на него и защита противника. И наоборот.
У них нет снаряжения, предметов, выносливости или здоровья. Просто атака против обороны.
Пример:
Боец 1:
Атака: 50, Защита: 35
Боец 2:
Атака 20, Оборона: 80
Боевой процесс будет только одной атакой, которая определит победителя. Таким образом, нет многократных атак или раундов. Я не хочу делать это детерминированным, но добавлю легкую версию неожиданного. Боец с более низкой атакой сможет выиграть другого бойца с более высокой защитой (но, конечно, не каждый раз)
Моей первой идеей было сделать его линейным и вызвать универсальный генератор случайных чисел.
If Random() < att1 / (att1 + def2) {
winner = fighter1
} else {
winner = fighter2
}
Например, с атакой 50 и защитой 80 у атакующего бойца будет около 38% на победу. Однако мне кажется, что неожиданность слишком далека, и худшие бойцы много выиграют.
Мне было интересно, как вы работали в подобных ситуациях.
PS Я много искал в этой QnA и других источниках, и я нашел похожие вопросы, упомянутые как слишком широкие для SE. Но у них было много атрибутов, оружия, предметов, классов и т. Д., Которые могли бы сделать его слишком сложным. Я думаю, что моя версия намного проще, чтобы соответствовать ей в стиле QnA SE.
Ответы:
Если вы хотите, чтобы результаты вашего боя были более предсказуемыми, но не полностью детерминированными, лучше всего систем.
Повторите время боя
n
(гдеn
должно быть неравное число) и объявите бойца победителем, который выиграл чаще. Чем больше ваша ценность,n
тем меньше сюрпризов вы получите.Эта система работает только в особом случае, когда бой является простым двоичным результатом выигрыша или проигрыша. Когда бой имеет более сложные результаты, например, когда победитель все еще теряет некоторые очки жизни, в зависимости от того, насколько близок был выигрыш, этот подход больше не работает. Более общее решение состоит в том, чтобы изменить способ генерации случайных чисел. Когда вы генерируете несколько случайных чисел, а затем берете среднее значение, результаты будут группироваться вблизи центра диапазона, а более экстремальные результаты будут более редкими. Например:
будет иметь такую кривую распределения:
(Фото любезно предоставлено Anydice - действительно полезным инструментом для разработки формул игровой механики, которые включают случайность, а не только для настольных игр)
В моем текущем проекте я использую вспомогательную функцию, которая позволяет установить произвольный размер выборки:
источник
+
вместо*
или я неправильно понял, что она делает?Это то, что я использовал, чтобы определить победителя в моем апплете Lords of Conquest Imitator. В этой игре, как и в вашей ситуации, есть только значение атаки и значение защиты. Вероятность того, что атакующий выиграет, тем больше, чем больше очков у атакующего, и чем меньше, тем больше очков у защиты, при равных значениях вероятности успеха атаки 50%.
Алгоритм
Переверните случайную монету.
1a. Головы: защита теряет очко.
1б. Хвосты: головы теряют очко.
Если и у защиты, и у атакующего все еще есть очки, вернитесь к шагу 1.
Тот, кто до 0 очков проигрывает битву.
3a. Атакующий до 0: атака не удалась.
3b. Защита до 0: атака успешна.
Я написал это на Java, но он должен быть легко переведен на другие языки.
Пример
Например, предположим, что att = 2 и def = 2, просто чтобы убедиться, что вероятность составляет 50%.
Битва будет решена с максимальным количеством
n = att + def - 1
подбрасываний монет или 3 в этом примере (это, по сути, лучший из 3 здесь). Есть 2 п возможных комбинаций монет переворачиваются. Здесь «W» означает, что атакующий выиграл бросок монеты, а «L» означает, что атакующий потерял бросок монеты.Атакующий выигрывает в 4/8, или в 50% случаев.
Математика
Математические вероятности, вытекающие из этого простого алгоритма, являются более сложными, чем сам алгоритм.
Количество комбинаций, где точно x Ls, определяется функцией комбинации:
Атакующий побеждает, когда есть между
0
иatt - 1
Ls. Количество выигрышных комбинаций равно сумме комбинаций от0
сквозногоatt - 1
, кумулятивного биномиального распределения:Вероятность атакующего выигрыш ш делится на 2 л , кумулятивной вероятности биномиального:
Вот код в Java , чтобы вычислить эту вероятность для произвольного
att
иdef
значений:Код тестирования:
Выход:
наблюдения
Скорее всего,
0.0
если у атакующего есть0
очки,1.0
если у атакующего есть очки, но у защиты есть0
очки,0.5
если очки равны, меньше, чем0.5
если у атакующего меньше очков, чем у защиты, и больше, чем0.5
если у атакующего больше очков, чем у защиты. ,Принимая
att = 50
иdef = 80
, мне нужно было переключиться наBigDecimal
s, чтобы избежать переполнения, но я получаю вероятность около 0,0040.Вы можете сделать вероятность ближе к 0,5, изменив
att
значение на среднее значениеatt
иdef
. Att = 50, Def = 80 становится (65, 80), что дает вероятность 0,1056.источник
Вы можете изменить атаку случайным числом, выбранным из нормального распределения. Таким образом, в большинстве случаев результатом будет то, что вы ожидаете, но иногда более высокая атака будет проигрывать против более низкой защиты, или более низкая атака будет выигрывать у более высокой защиты. Вероятность этого будет уменьшаться с увеличением разницы между атакой и защитой.
Функция
norm(x0, sigma)
возвращает число с плавающей точкой, отобранное из нормального распределения с центром в точке x0, со сигма стандартного отклонения. Большинство языков программирования предоставляют библиотеку с такой функцией, но если вы хотите сделать это самостоятельно, взгляните на этот вопрос . Вы должны настроить сигму таким образом, чтобы она выглядела «правильно», но значение 10-20 может быть хорошим началом.Для нескольких значений сигма вероятность победы для данного
att1 - def2
выглядит следующим образом:источник