Я разработчик веб-игр, и у меня возникла проблема со случайными числами. Допустим, у игрока есть 20% шанс получить критический удар своим мечом. Это означает, что 1 из 5 попаданий должен быть критическим. Проблема в том, что я получил очень плохие результаты в реальной жизни - иногда игроки получают 3 крита в 5 попаданиях, иногда ни одного в 15 попаданиях. Сражения довольно короткие (3-10 попаданий), поэтому важно получить хорошее случайное распределение.
В настоящее время я использую PHP mt_rand()
, но мы просто перемещаем наш код на C ++, поэтому я хочу решить эту проблему в новом движке нашей игры.
Я не знаю, является ли решение каким-то равномерным генератором случайных чисел, или, может быть, вспомнить предыдущие случайные состояния, чтобы вызвать правильное распределение.
Ответы:
Я согласен с более ранними ответами, что реальная случайность в небольших сериях некоторых игр нежелательна - она кажется слишком несправедливой для некоторых случаев использования.
Я написал простую реализацию типа Shuffle Bag в Ruby и провел некоторое тестирование. Реализация сделала это:
Это считается несправедливым на основе граничных вероятностей. Например, для вероятности 20% вы можете установить 10% в качестве нижней границы и 40% в качестве верхней границы.
Используя эти границы, я обнаружил, что при прогонах из 10 попаданий в 14,2% случаев истинная псевдослучайная реализация давала результаты, выходящие за эти границы . Приблизительно в 11% случаев 0 критических попаданий были забиты в 10 попытках. В 3,3% случаев 5 или более критических попаданий попадали из 10. Естественно, при использовании этого алгоритма (с минимальным количеством бросков 5) гораздо меньшее количество (0,03%) «честных» пробегов вышло за пределы , Даже если приведенная ниже реализация не подходит (конечно, можно делать более умные вещи), стоит отметить, что очень часто ваши пользователи будут чувствовать, что это несправедливо по отношению к реальному псевдослучайному решению.
Вот мясо моего
FairishBag
написано на Ruby. Полная реализация и быстрое моделирование Монте-Карло доступно здесь (суть) .Обновление: использование этого метода увеличивает общую вероятность получения критического удара примерно до 22% с использованием указанных выше границ. Вы можете компенсировать это, установив его «реальную» вероятность чуть ниже. Вероятность 17,5% с честной модификацией дает наблюдаемую долгосрочную вероятность около 20% и сохраняет краткосрочные прогнозы на должном уровне.
источник
Что вам нужно, так это сумка для случайного выбора . Это решает проблему истинной случайности, которая слишком случайна для игр.
Алгоритм примерно такой: вы кладете 1 критическое и 4 некритических попадания в сумку. Затем вы рандомизируете их порядок в сумке и выбираете их по одному. Когда сумка пуста, вы снова наполняете ее теми же значениями и рандомизируете. Таким образом, вы получите в среднем 1 критическое попадание за 5 попаданий, и самое большее 2 критических и 8 некритических попаданий подряд. Увеличьте количество предметов в сумке для большей случайности.
Вот пример реализации (на Java) и ее тестовых случаев, которые я написал некоторое время назад.
источник
Вы неправильно понимаете, что означает случайное.
Что из этого является более случайным?
В то время как второй график выглядит более равномерно распределенным, на самом деле более случайным является первый график. Человеческий разум часто видит шаблоны в случайном порядке, поэтому мы видим сгустки на первом графике как шаблоны, но это не так - они просто являются частью случайно выбранной выборки.
источник
Учитывая поведение, которое вы просите, я думаю, что вы случайным образом выбираете не ту переменную.
Вместо того, чтобы рандомизировать, будет ли этот удар критическим, попробуйте рандомизировать количество ходов до следующего критического удара. Например, просто выбирайте число от 2 до 9 каждый раз, когда игрок получает критическое значение, а затем дайте ему его следующее критическое значение после того, как прошло много раундов. Вы также можете использовать методы игры в кости, чтобы приблизиться к нормальному распределению - например, вы получите свой следующий критический результат в ходах 2D4.
Я полагаю, что эта техника также используется в RPG, в которых есть случайные столкновения и в потустороннем мире - вы случайным образом выбираете счетчик шагов, и после стольких шагов вы получаете удар снова. Это кажется намного более справедливым, потому что вы почти никогда не будете поражены двумя встречами подряд - если это случится хотя бы один раз, игроки становятся раздражительными.
источник
Сначала определите «правильное» распределение. Случайные числа, ну, в общем, случайные - результаты, которые вы видите, полностью согласуются с (псевдо) случайностью.
Продолжая это, я предполагаю, что вам нужно ощущение "справедливости", поэтому пользователь не может пройти 100 ходов без успеха. Если так, я бы отслеживал количество сбоев с момента последнего успеха и оценивал полученный результат. Давайте предположим, что вы хотите, чтобы 1 из 5 бросков «преуспел». Таким образом, вы случайным образом генерируете число от 1 до 5, и если это 5, отлично.
Если нет, запишите ошибку и в следующий раз сгенерируйте число от 1 до 5, но добавьте, скажем, floor (numFailures / 2). Так что на этот раз у них снова есть шанс 1 к 5. Если они терпят неудачу, в следующий раз интервал выигрыша 4 и 5; 2 из 5 шансов на успех. При таком выборе после 8 неудач они обязательно преуспеют.
источник
Как насчет замены mt_rand () на что-то подобное?
(RFC 1149.5 определяет 4 как стандартное случайное число, проверенное IEEE.)
Из XKCD .
источник
Надеюсь, эта статья поможет вам: http://web.archive.org/web/20090103063439/http://www.gamedev.net:80/reference/design/features/randomness/
Этот метод генерации «случайных чисел» распространен в играх RPG / MMORPG.
Проблема, которую он решает, заключается в следующем (извлечение):
источник
То, что вы хотите, это не случайные числа, а числа, которые кажутся человеку случайными. Другие уже предложили отдельные алгоритмы, которые могут вам помочь, например, Shuffle Bad.
Для хорошего подробного и всестороннего анализа этой области см. Мудрость программирования игр AI 2 . Вся книга достойна прочтения любым разработчиком игры, идея «на первый взгляд случайных чисел» рассматривается в главе:
Отфильтрованная случайность для решений AI и игровой логики :
Аннотация: Традиционная мудрость предполагает, что чем лучше генератор случайных чисел, тем более непредсказуемой будет ваша игра. Однако, согласно исследованиям психологии, истинная случайность в краткосрочной перспективе часто выглядит совершенно неслучайно для людей. В этой статье показано, как сделать случайные решения AI и игровую логику более случайными для игроков, сохраняя при этом сильную статистическую случайность.
Вы также можете найти другую интересную главу:
Статистика случайных чисел
Аннотация: случайные числа наиболее интенсивно используются искусственным интеллектом и играми в целом. Игнорировать их потенциал - значит делать игру предсказуемой и скучной. Неправильное их использование может быть столь же вредным, как и полное их игнорирование. Понимание того, как генерируются случайные числа, их ограничения и возможности, может устранить многие трудности при их использовании в вашей игре. Эта статья предлагает понимание случайных чисел, их генерации и методов, чтобы отделить хорошие от плохих.
источник
Конечно, у любого поколения случайных чисел есть шанс произвести такие прогоны? Вы не получите достаточно большой набор образцов в 3-10 рулонах, чтобы увидеть соответствующий процент.
Возможно, вам нужен порог милосердия ... запомните последние 10 бросков, и если у них не было критического удара, дайте им халяву. Сгладить стропы и стрелы случайности.
источник
Лучшее решение может быть игра-тестирование с несколькими различных не случайными схемами и выбрать тот , который делает игрок счастливых.
Вы также можете попробовать политику отмены для того же номера в данной схватке, например, если игрок бросает в
1
свой первый ход, принять его. Чтобы получить другой,1
им нужно бросить 21
с подряд. Чтобы получить треть,1
им нужно 3 подряд, до бесконечности.источник
К сожалению, то, что вы просите, - это генератор неслучайных чисел, потому что вы хотите, чтобы предыдущие результаты были приняты во внимание при определении следующего числа. Боюсь, это не так, как работают генераторы случайных чисел.
Если вы хотите, чтобы 1 из каждых 5 попаданий была критической, просто выберите число от 1 до 5 и скажите, что это попадание будет критическим.
источник
mt_rand () основана на реализации Mersenne Twister , что означает, что она дает одно из лучших случайных распределений, которое вы можете получить.
Очевидно, что вы хотите совсем не случайность, поэтому вы должны начать с точного указания того, что вы хотите. Вы, вероятно, поймете, что у вас противоречивые ожидания - что результаты должны быть действительно случайными и не предсказуемыми, но в то же время они не должны демонстрировать локальные отклонения от заявленной вероятности - но тогда это становится предсказуемым. Если вы установили максимум 10 не-критов подряд, то вы просто сказали игрокам: «Если у вас было 9 не-критов подряд, следующий будет критическим со 100% уверенностью» - вы можете ну вообще не заморачивайся случайностью.
источник
При таком небольшом количестве тестов вы должны ожидать такие результаты:
Истинная случайность предсказуема только при огромном размере набора, так что вполне возможно бросить монету и получить головы 3 раза подряд, однако после нескольких миллионов подбрасываний вы получите примерно 50-50.
источник
Я вижу много ответов, предлагающих отследить ранее сгенерированные числа или перемешать все возможные значения.
Лично я не согласен, что 3 крита подряд это плохо. Также я не согласен, что 15 не критов подряд это плохо.
Я бы решил эту проблему, изменив шанс крита сам после каждого номера. Пример (чтобы продемонстрировать идею):
Чем дольше вы не получаете крита, тем больше у вас шансов на следующее действие. Сброс, который я включил, является полностью необязательным, и он должен был бы проверить, чтобы определить, нужен он или нет. Может быть или не быть желательным давать более высокую вероятность крита для более чем одного действия подряд после длинной цепочки некритовых действий.
Просто добавляю мои 2 цента ...
источник
Несколько лучших ответов - отличные объяснения, поэтому я просто сосредоточусь на алгоритме, который дает вам контроль над вероятностью «плохих полос», но никогда не становится детерминированным. Вот что я думаю тебе следует сделать:
Вместо указания p , параметра распределения Бернулли, которое является вашей вероятностью критического удара, укажите a и b , параметры бета-распределения, «сопряженный априор» распределения Бернулли. Вы должны отслеживать A и B , количество критических и некритических попаданий на данный момент.
Теперь, чтобы указать а и б , убедитесь, что a / (a + b) = p, шанс критического удара. Важно то, что (a + b) количественно определяет, насколько близко вы хотите, чтобы A / (A + B) было к p в целом.
Вы делаете свою выборку так:
пусть
p(x)
будет функцией плотности вероятности бета-распределения. Он доступен во многих местах, но вы можете найти его в GSL как gsl_ran_beta_pdf.Выберите критическое попадание путем выборки из распределения Бернулли с вероятностью p_1 / (p_1 + p_2)
Если вы обнаружите, что у случайных чисел слишком много «плохих полос», увеличьте a и b , но в пределе, когда a и b уходят в бесконечность, вы будете использовать ранее описанный подход с мешочками.
Если вы реализуете это, пожалуйста, дайте мне знать, как это происходит!
источник
Если вы хотите распределение, которое не поддерживает повторные значения, вы можете использовать простой алгоритм повторного отклонения.
например
Этот код отклоняет повторяющиеся значения в 95% случаев, делая повторения маловероятными, но не невозможными. Статистически это немного некрасиво, но, вероятно, даст желаемые результаты. Конечно, это не помешает распространению типа "5 4 5 4 5". Вы можете стать хитрее и отказаться от второго (скажем) 60% времени, а третьего - от 30%.
Я не рекомендую это как хороший дизайн игры. Просто предлагая, как достичь того, что вы хотите.
источник
Не совсем понятно, чего ты хочешь. Можно создать функцию так, чтобы первые 5 раз, когда вы ее вызывали, она возвращала числа 1-5 в случайном порядке.
Но это не совсем случайно. Игрок будет знать, что он получит ровно одну 5 в следующих 5 атаках. Хотя это может быть тем, что вы хотите, и в этом случае вам просто нужно написать код самостоятельно. (создайте массив, содержащий числа, а затем перемешайте их)
В качестве альтернативы, вы можете продолжать использовать свой текущий подход и предположить, что ваши текущие результаты связаны с плохим генератором случайных чисел. Обратите внимание, что ничего не может быть не так с вашими текущими номерами. Случайные значения являются случайными. иногда вы получаете 2, 3 или 8 одинаковых значений подряд. Потому что они случайные. Хороший генератор случайных чисел просто гарантирует, что в среднем все числа будут возвращаться одинаково часто.
Конечно, если вы использовали плохой генератор случайных чисел, это могло исказить ваши результаты, и если это так, просто переключение на лучший генератор случайных чисел должно решить проблему. (Проверьте библиотеку Boost.Random для лучших генераторов)
Кроме того, вы можете вспомнить последние N значений, возвращаемых вашей случайной функцией, и взвесить результат по ним. (простой пример: «для каждого появления нового результата есть 50% -й шанс, что мы должны отбросить значение и получить новое»)
Если бы мне пришлось угадывать, я бы сказал, что придерживаться «фактической» случайности - ваш лучший выбор. Убедитесь, что вы используете хороший генератор случайных чисел, а затем продолжайте в том же духе, что и сейчас.
источник
Вы можете создать список, содержащий числа от 1 до 5, и отсортировать их по случайности. Затем просто просмотрите список, который вы создали. У вас есть гарантия того, что вы наберете каждый номер хотя бы один раз ... Когда вы закончите с первыми 5, просто создайте еще 5 номеров ...
источник
Я рекомендую прогрессивную процентную систему, которую использует Blizzard: http://www.shacknews.com/onearticle.x/57886
Обычно вы бросаете ГСЧ, а затем сравниваете его со значением, чтобы определить, успешен он или нет. Это может выглядеть так:
Все, что вам нужно сделать, это добавить прогрессивное увеличение базового шанса ...
Если вам нужно, чтобы он был более модным, его легко добавить еще. Вы можете ограничить сумму, которую может получить прогрессивный шанс, чтобы избежать 100% критического шанса или сбросить его при определенных событиях. Вы также можете увеличивать прогрессивный шанс в меньших количествах при каждом повышении, например, прогрессивный шанс + = (1 - прогрессивный шанс) * МАСШТАБ, где МАСШТАБ <1.
источник
Ну, если вы немного в математике, вы можете попробовать экспоненциальное распределение
Например, если лямбда = 0,5, ожидаемое значение равно 2 (прочитайте эту статью!), Это означает, что вы, скорее всего, будете нажимать / критические / что угодно в течение каждого второго хода (например, 50%, да?). Но с таким распределением вероятностей вы точно определите, что пропустили (или сделаете против чего-либо еще) на 0-м ходу (тот, в котором событие уже произошло, и поворот_счетчика был сброшен), с вероятностью 40% попасть в следующий ход, около 65% шанс сделать это 2-й (следующий после следующего) ход, около 80%, чтобы попасть 3-й и так далее.
Основная цель этого распределения состоит в том, что если у человека есть 50% -й шанс попадания, и он пропускает 3 раза подряд, он будет уверенно (ну, шанс более 80%, и он увеличивается с каждым следующим ходом). Это приводит к более «справедливым» результатам, сохраняя общую вероятность 50% без изменений.
Принимая ваш шанс крита 20%, у вас есть
По-прежнему около 0,2% (против этих 5%) шансов 3 крита + 2 не крита в 5 последовательных ходах. И есть вероятность 14% для 4 последовательных не критов, 5% из 5, 1,5% для 6, 0,3% для 7, 0,07% для 8 последовательных не критов. Бьюсь об заклад, его «более справедливо», чем 41%, 32%, 26%, 21% и 16%.
Надеюсь, тебе до сих пор не скучно до смерти.
источник
Как насчет того, чтобы вероятность критического удара зависела от последних N атак. Одна простая схема - это некая цепочка Маркова: http://en.wikipedia.org/wiki/Markov_chain, но в любом случае код очень прост.
Конечно, вы должны сделать свою математику, потому что вероятность критического удара ниже, чем вероятность критического, если вы знаете, что было достаточно ходов с момента последнего
источник
OP,
В значительной степени, если вы хотите, чтобы это было честно, это не будет случайным.
Проблема вашей игры - фактическая длина матча. Чем дольше совпадение, тем меньше случайности вы увидите (криты будут составлять 20%), и это приблизится к вашим предполагаемым значениям.
У вас есть два варианта, предварительно рассчитать атаки на основе предыдущих бросков. Который вы будете получать один крит каждые 5 атак (на основе ваших 20%), но вы можете сделать порядок, он происходит случайно.
listOfFollowingAttacks = {Hit, Hit, Hit, Miss, Crit};
Это шаблон, который вы хотите. Поэтому сделайте так, чтобы он выбирал случайным образом из этого списка, пока он не опустеет, а затем создайте его заново.
Это шаблон, который я создал для своей игры, и он работает достаточно хорошо для того, что я хочу.
ваш второй вариант - увеличить шанс критического удара, вы, вероятно, увидите более четное число в конце всех атак (при условии, что ваши матчи заканчиваются довольно быстро). Чем меньше вероятность, тем больше RNG вы получите.
источник
Вы смотрите на линейное распределение, когда вы, вероятно, хотите нормальное распределение.
Если вы помните, еще в юности, играя в D & D, вас попросили бросить несколько n-сторонних кубиков, а затем суммировать результаты.
Например, бросание 4 х 6-сторонней матрицы отличается от бросания 1 х 24-сторонней игры в кости.
источник
В City of Heroes на самом деле есть механик под названием «Разрушитель полос», который решает именно эту проблему. Это работает так, что после пропущенной строки длины, связанной с самой низкой вероятностью попадания в строку, следующая атака гарантированно будет попаданием. Например, если вы пропустите атаку с вероятностью попадания более 90%, то ваша следующая атака будет автоматически попадать, но если ваш шанс попадания будет ниже, чем 60%, вам понадобится несколько последовательных промахов, чтобы вызвать «полосатик» (I не знаю точных цифр)
источник
это действительно предсказуемо ... но вы никогда не можете быть уверены.
источник
Как насчет взвешивания стоимости?
Например, если у вас есть шанс 20% на критическое попадание, сгенерируйте число от 1 до 5 с одним числом, представляющим критический удар, или число от 1 до 100 с 20 числами, являющимся критическим попаданием.
Но пока вы работаете со случайными или псевдослучайными числами, невозможно избежать результатов, которые вы видите в данный момент. Это природа случайности.
источник
Реакция на: «Проблема в том, что я получил очень плохие результаты в реальной жизни - иногда игроки получают 3 крита в 5 попаданиях, иногда ни одного в 15 попаданиях».
У вас есть шанс где-то между 3 и 4% получить ничего в 15 попаданиях ...
источник
Я хотел бы предложить следующую «произвольно отсроченную ставку»:
in-array
) изначально заполнен значениями от 0 до n-1, другой (out-array
) пустin-array
in-array
вout-array
out-array
обратно вin-array
Это имеет свойство, что он будет «реагировать» медленнее, чем больше n . Например, если вам нужен шанс 20%, установка n на 5 и попадание в 0 «менее случайны», чем установка n в 10 и попадание в 0 или 1, а от 0 до 199 из 1000 будет почти неотличима от истинной случайности по небольшой выборке. Вам нужно будет настроить п вашего размера выборки.
источник
Предварительно рассчитать случайный критический удар для каждого игрока.
источник
Я думаю, что, возможно, вы используете неправильную функцию случайного распределения. Вы, вероятно, не хотите равномерного распределения по числам. Вместо этого попробуйте нормальное распределение, чтобы критические попадания стали более необычными, чем «обычные» попадания.
Я работаю с Java, так что я не уверен, где можно найти что-то для C ++, которое дает вам случайные числа с нормальным распределением, но должно быть что-то там.
источник