Як покращити це генерація випадкових чисел у моєму контексті?


11

У моїй грі є слово вгорі екрана, зверху йдуть літери, і користувач повинен торкнутися літер, щоб завершити слово.

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

Я хочу, щоб випадкові числа, які я створюю, частіше генерували потрібні літери, щоб гравцеві не довелося витрачати цілий день, щоб виконати одне слово.

Я спробував такі методи:

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

  2. Майте змінну селектора, значення якої знаходиться в діапазоні [0..2], генерованому випадковим чином, якщо selector == 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
    }
}

Імовірність контролює, наскільки вірогідний даний виклик на SelectLetter дасть вам слово слово - на рівні 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", тому що вони цього не потребують!

Звідси решта налаштовується на ваш дизайн.


Оце Так! як ви навіть придумали це?: p Я завтра перевіру ваш метод завтра вранці, і, думаю, він спрацює, я надам більше відгуків, як тільки я протестую цей меход. Дуже дякую за таку відповідь.
Даніял Азрам

4
Статистика! Як дизайнер ігор, чи програміст, статистиці дуже варто вивчити. Принаймні, розуміння ймовірності (і як їх поєднувати) є надзвичайно корисним.
ACEfanatic02

1
Приходьте запропонувати те саме. Чудова відповідь. Також пам’ятайте, що таке рішення може бути способом впровадження рівнів складності. Легкий = 0,5, середній = 0,25, важкий = 0,10. тощо
Тасос

Я не згоден, що це не випадково. Це випадковий розподіл, це лише кусочне розподіл, тоді як ми зазвичай думаємо про рівномірний розподіл. І для додання вашої ідеї я б пішов далі, ніж просто "Виберіть випадкову букву в слово", я б поширив це по літерах, які зустрічаються найбільше. Наприклад, "mississippi" потрібно більше s, і я вибираю більше з них.
Блейн


21

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

Простий спосіб здійснити це - розмістити всі літери в послідовному рядку, причому кожна буква з’являється так часто, як потрібно, а потім дозволити вашому RNG взяти лист з випадкової позиції. Приклад псевдокоду:

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

char randomLetter = letters[randomIntegerBetween(0, letters.length - 1)];

2
+1 Це хороша концепція, але я підозрюю, що є більш елегантна реалізація.
Еворлор


Для більш тонкодисперсного розподілу можна зберігати таблицю частот літер, масштабованих таким чином, щоб вони дорівнювали 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 у стандартній бібліотеці, якщо ви використовуєте іншу мову, це не важко реалізувати).

Якщо ви хочете, щоб літери в слові з’являлися швидше, покладіть більше масивів слово букв у масив.


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';
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.