Как улучшить генерацию случайных чисел в моем контексте?

11

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

В настоящее время я генерирую буквы случайным образом (на самом деле случайные числа и числа являются индексом для массива букв. Например: 0 = a, 1 = b), но проблема в том, что требуется слишком много времени, чтобы получить все необходимые буквы для завершения слово.

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

Я пробовал следующие методы:

  1. Определить все буквы в слове (слово всегда длиной 6 букв), сгенерировать массив индексов длиной 6, присвоить каждому индексу массива случайное число от буквы-2 до буквы + 2 и в конце выбрать случайным образом один индекс из массива, чтобы показать.

  2. Имейте переменную селектора, значение которой находится в диапазоне [0..2], генерируемую случайным образом, если селектор == 0, тогда обнаруживайте буквы, которые составляют слово, и случайным образом выбирайте одну букву, иначе случайным образом получите любой алфавит из az.

Оба эти метода не оказали мне никакой помощи. Я буду очень рад, если вы сможете мне помочь.

Спасибо за прочтение, надеюсь, вы поняли вопрос, и я жду ответа.

Даниял Азрам
источник
2
«Оба эти метода не оказали мне никакой помощи». Почему бы и нет? Что не работает с этими методами?
Vaillancourt
Я не знаю почему, но это все еще занимает слишком много времени, как 1 минута, чтобы получить все необходимые алфавиты.
Даниял Азрам
@DaniyalAzram Вам, вероятно, следует увеличить частоту еще немного, если эти буквы появляются не так часто, как кажется, в этом ваша проблема.
JFA

Ответы:

21

Вы на самом деле не хотите случайного распределения. Я говорю об этом прямо, потому что то, что мы считаем «случайным» для дизайна, обычно не является истинной случайностью.

Теперь, имея это в виду, давайте добавим некоторые тонкие настройки - это те вещи, с которыми вы будете возиться, пока дизайн не станет «правильным».

ChooseLetter() {
    const float WordLetterProbability = 0.5f;
    if (Random.NextFloat01() < WordLetterProbability) {
        // Pick a random letter in the word
    }
    else {
        // Pick a random letter *not* in the word
    }
}

Вероятность определяет, насколько вероятно, что данный вызов ChooseLetter даст вам слово-букву - при 0,5 вы будете получать слово-слово примерно каждый раз. В 0.25 каждый четвертый будет словом, буквой и т. Д.

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

const float BaseWordLetterProbability = 0.5f;
const float WordLetterProbabilityIncrease = 0.25f;
float WordLetterProbability = BaseWordLetterProbability;
ChooseLetter() {
    if (Random.NextFloat01() < WordLetterProbability) {
        // Pick a random letter in the word
        WordLetterProbability = BaseWordLetterProbability;
    }
    else {
        // Pick a random letter *not* in the word
        WordLetterProbability += WordLetterProbabilityIncrease;
    }
}

Итак, теперь мы никогда не идем больше, чем две буквы без слова буквы. (Потому что после двух промахов у нас есть 1,0 вероятность получить слово буква.)

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

Например, если слово «тест», а у игрока уже есть «s», мы не хотим давать им другие «s», потому что им это не нужно!

Отсюда все остальное настраивается под ваш дизайн.

ACEfanatic02
источник
Вот Это Да! как ты вообще это придумал?: p Я проверю твой метод завтра утром и думаю, что он сработает, я предоставлю больше отзывов, как только протестирую этот метод. Большое спасибо за такой ответ.
Даниял Азрам
4
Статистика! Как гейм-дизайнер, так и программист, статистику очень стоит изучить. По крайней мере, понимание вероятности (и как их объединить) чрезвычайно полезно.
ACEfanatic02
1
Приходите, чтобы предложить то же самое. Отличный ответ. Также имейте в виду, что подобное решение может быть способом введения уровней сложности. Легкий = 0,5, средний = 0,25, жесткий = 0,10. и т. д.
Тасос
Я не согласен, что это не случайно. Это случайное, это просто кусочное распределение, в то время как мы обычно думаем о равномерном распределении. И чтобы добавить к вашей идее, я бы пошел дальше, чем просто «Выбрать случайную букву в слове», я бы распределил ее по буквам, которые встречаются чаще всего. Например, «Миссисипи» нужно больше s, и я выбираю больше.
Блейн
21

Вы можете взвесить вероятность всех ваших писем в соответствии с частотой, с которой они встречаются на языке, на котором написаны ваши слова. Хорошим ориентиром является набор скрабблов . Английская версия, например, имеет 12 E, но только один Z и один Q.

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

const String letters = "AAAAAAAAABBCCDDDDEEEEEEEEEEEEFFGGGHHIIIIIIIIIJ/*...and so on...*/"

char randomLetter = letters[randomIntegerBetween(0, letters.length - 1)];
Philipp
источник
2
+1 Это хорошая концепция, но я подозреваю, что есть более элегантная реализация.
Evorlor
1
Связанный: codereview.stackexchange.com/q/94202/61072
Evorlor
Для более детального распределения вы можете хранить таблицу частот букв масштабированных таким образом, чтобы они составляли 1, генерировать случайное число от 0 до 1 и циклически перемещаться по таблице, вычитая частоту каждой буквы из случайного числа до становится нулевым или отрицательным. Вы даже можете оптимизировать это, отсортировав самые распространенные буквы сначала в таблице. Или используйте что-то вроде метода псевдонимов, чтобы полностью не зацикливаться на таблице.
Илмари Каронен
4
Это не решило бы проблему. Проблема в том, что пользователю нужны конкретные буквы, чтобы закончить игру. Скажи, им нужен Z? Что тогда? Это только усложнит работу с редкими буквами, делая пользователя еще более разочарованным.
AmazingDreams
@AmazingDreams указывают на хорошую вещь, но мы можем немного ее изменить, поэтому я назначу больший вес требуемым алфавитам и меньший вес другим. Я должен сказать, что это очень хорошая концепция для подражания.
Даниял Азрам
4

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

Вместо того, чтобы просто выбрать случайное письмо:

  1. выбрать случайную букву A
  2. выбрать случайное число X
  3. если X > k и A нет [list of remaining needed letters], попробуйте еще раз на 1.

Чем меньше k, тем чаще Aбудет действительно последняя буква .

Чтобы настроить алгоритм, поиграйте с любым значением k, например k = 0.5 . Если вам кажется, что игра слишком сложная, попробуйте0.4 вместо этого и т. Д., Пока не найдете разумное значение. Это также напрямую дает вам настройку сложности , которую, например, вы можете увеличить по мере продвижения игрока в игре.

Сэм Хоцевар
источник
Спасибо за ответ, но это похоже на ответ ACEfanatic02.
Даниял Азрам
3

Простой способ гарантировать, что требуемые буквы появятся в течение заданного времени, состоит в том, чтобы использовать заполнение массива буквами в слове и остальной части алфавита (возможно, повторяется), затем случайным образом перемешать массив (c ++ имеет std :: random_shuffle в стандартной библиотеке, если вы используете другой язык, это не сложно реализовать).

Если вы хотите, чтобы буквы в слове появлялись быстрее, поместите в массив больше копий букв-слов.

GuyRT
источник
0

Если вы используете C ++, вы можете использовать уже существующий дистрибутив http://en.cppreference.com/w/cpp/numeric/random/piecewise_constant_distribution

Он также амортизирует сложность с постоянным временем, которая лучше, чем наивные методы. пример:

#include <iostream>
#include <string>
#include <map>
#include <random>
#include <numeric>

int main()
{
    constexpr double containedLetterWeight = 3.0;
    constexpr int iterations = 10000;
    std::string word = "DISTRIBUTION";

    std::mt19937 rng(123);

    std::vector<double> values('Z' - 'A' + 2);
    std::iota(values.begin(), values.end(), 0.0);

    std::vector<double> weights('Z' - 'A' + 1, 1.0);
    for(const auto& c : word) weights[c - 'A'] = containedLetterWeight;

    std::piecewise_constant_distribution<> dist(values.begin(), values.end(), weights.begin());

    std::map<char, int> results;
    for(int n = 0; n < iterations; ++n)
    {
        ++results[static_cast<char>(dist(rng)) + 'A'];
    }
    for(const auto& p : results)
    {
        std::cout << p.first << ' ' << static_cast<float>(p.second) / iterations << '\n';
    }
}
Sopel
источник