Comment améliorer cette génération de nombres aléatoires dans mon contexte?

11

Dans mon jeu, il y a un mot en haut de l'écran, des lettres pleuvent du haut et l'utilisateur doit toucher les lettres pour terminer le mot.

Actuellement, je génère des lettres de manière aléatoire (en fait, des nombres et des nombres aléatoires sont l'index du tableau de lettres. Par exemple: 0 = a, 1 = b) mais le problème est qu'il faut trop de temps pour obtenir toutes les lettres requises pour terminer la mot.

Ce que je veux, c'est que les nombres aléatoires que je génère devraient générer plus souvent les lettres requises afin que le joueur n'ait pas à passer toute la journée pour terminer un mot.

J'ai essayé les méthodes suivantes:

  1. Détectez toutes les lettres du mot (le mot a toujours 6 lettres), générez le tableau d'index de longueur 6, affectez chaque index du tableau à un nombre aléatoire de la lettre-2 à la lettre + 2 et à la fin choisissez au hasard un index du tableau pour montrer.

  2. Avoir une variable de sélecteur dont la valeur est dans la plage [0..2], générée aléatoirement, si sélecteur == 0 alors détecter les lettres qui font le mot et choisir aléatoirement une lettre, sinon obtenir au hasard n'importe quel alphabet de az.

Ces deux méthodes ne m'ont pas aidé. Je serai très heureux si vous pouvez m'aider.

Merci d'avoir lu ceci, j'espère que vous avez compris la question et j'attends la réponse.

Daniyal Azram
la source
2
"ces deux méthodes ne m'ont pas aidé" Pourquoi pas? Qu'est-ce qui n'a pas fonctionné avec ces méthodes?
Vaillancourt
Je ne sais pas pourquoi mais cela prend encore trop de temps comme 1 minute pour obtenir tous les alphabets requis.
Daniyal Azram
@DaniyalAzram, vous devriez probablement augmenter encore la fréquence si ces lettres n'apparaissent pas assez souvent, car cela semble être le problème.
JFA

Réponses:

21

Vous ne voulez pas réellement de distribution aléatoire. Je le souligne de manière explicite, car ce que nous considérons comme "aléatoire" pour la conception n'est généralement pas le vrai hasard.

Maintenant, avec cela à l'esprit, ajoutons quelques valeurs de peaufinage - ce sont des choses avec lesquelles vous jouerez jusqu'à ce que le design soit "correct".

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
    }
}

La probabilité contrôle la probabilité qu'un appel donné à ChooseLetter vous donne une lettre de mot - à 0,5, vous obtiendrez une lettre de mot à peu près toutes les deux fois. À 0,25, un sur quatre sera une lettre de mot, etc.

C'est encore un peu simpliste - parce que le caractère aléatoire est, eh bien, aléatoire , vous n'avez en fait aucune garantie sur le temps que vous passerez entre les lettres de mots. (En théorie, vous pouvez passer indéfiniment sans une lettre de mot, c'est juste très très peu probable.) Au lieu de cela, nous pouvons ajouter un facteur pour augmenter la probabilité d'une lettre de mot chaque fois que nous n'en obtenons pas:

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;
    }
}

D'accord, alors maintenant nous n'allons jamais plus de deux lettres sans une lettre de mot. (Parce que, après deux échecs, nous avons une probabilité de 1,0 d'obtenir une lettre de mot.)

Enfin, nous devons prendre en considération le fonctionnement du choix de la lettre dans un mot. Afin de fournir au joueur les lettres dont il a réellement besoin, nous devons supprimer les lettres de l'ensemble au fur et à mesure qu'elles les reçoivent.

Par exemple, si le mot est "test" et que le joueur a déjà des "s", nous ne voulons pas leur en donner d'autres, car ils n'en ont pas besoin!

De là, le reste est peaufiné pour s'adapter à votre conception.

ACEfanatic02
la source
Hou la la! comment avez-vous trouvé ça?: p Je testerai votre méthode demain matin et je pense que cela fonctionnera, je fournirai plus de feedback une fois que je testerai cette méthode. Merci beaucoup pour cette réponse.
Daniyal Azram
4
Statistiques! Soit comme un concepteur de jeu ou programmeur, les statistiques sont très apprentissage vaut la peine. À tout le moins, une compréhension des probabilités (et comment les combiner) est extrêmement utile.
ACEfanatic02
1
Venez suggérer la même chose. Très bonne réponse. Gardez également à l'esprit qu'une solution comme celle-ci pourrait être un moyen d'introduire des niveaux de difficulté. Facile = 0,5, moyen = 0,25, dur = 0,10. etc
Tasos
Je ne suis pas d'accord que ce n'est pas aléatoire. C'est aléatoire, c'est juste une distribution par morceaux alors que nous pensons généralement à la distribution uniforme. Et pour ajouter à votre idée, j'irais plus loin que "Choisir une lettre au hasard dans le mot", je distribuerais cela par les lettres qui se produisent le plus. Par exemple, "mississippi" a besoin de plus de s et de i, alors choisissez-en plus.
Blaine
21

Vous pouvez pondérer la probabilité de toutes vos lettres en fonction de la fréquence à laquelle elles apparaissent dans la langue dans laquelle vos mots sont. Une bonne ligne directrice est le jeu de scrabble . La version anglaise, par exemple, a 12 E mais seulement un Z et un Q.

Un moyen simple de mettre en œuvre cela est de mettre toutes les lettres dans une chaîne consécutive, chaque lettre apparaissant aussi souvent que vous le souhaitez, puis demandez à votre RNG de prendre une lettre à partir d'une position aléatoire. Exemple de pseudocode:

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

char randomLetter = letters[randomIntegerBetween(0, letters.length - 1)];
Philipp
la source
2
+1 C'est un bon concept, mais je pense qu'il existe une implémentation plus élégante.
Evorlor
Pour une distribution plus fine, vous pouvez stocker une table de fréquences de lettres mises à l'échelle de manière à ce qu'elles totalisent 1, générer un nombre aléatoire de 0 à 1 et boucler sur la table en soustrayant la fréquence de chaque lettre du nombre aléatoire jusqu'à ce qu'elle devient nul ou négatif. Vous pouvez même optimiser cela en triant les lettres les plus courantes en premier dans le tableau. Ou utilisez quelque chose comme la méthode alias pour éviter de boucler entièrement sur la table.
Ilmari Karonen
4
Cela ne résoudrait pas le problème. Le problème est que l'utilisateur a besoin de lettres spécifiques pour terminer le jeu. Disons qu'ils ont besoin d'un Z? Et alors? Cela rendrait les lettres rares plus difficiles à obtenir, rendant l'utilisateur encore plus frustré.
AmazingDreams
@AmazingDreams souligne une bonne chose mais nous pouvons le modifier un peu donc je vais attribuer plus de poids aux alphabets requis et moins de poids aux autres. Je dois dire que c'est un très bon concept à suivre.
Daniyal Azram
4

Voici une façon de l'améliorer en utilisant un seul paramètre kque vous pouvez modifier.

Au lieu de simplement choisir une lettre au hasard:

  1. choisir une lettre au hasard A
  2. choisir un nombre aléatoire X
  3. si X > k et A est pas [list of remaining needed letters], essayez à nouveau à 1.

Plus elle kest petite , plus la lettre finale Asera souvent celle qui sera réellement nécessaire.

Pour modifier l'algorithme, jouez avec n'importe quelle valeur pour k, par exemple k = 0.5 . Si vous trouvez que le jeu est trop difficile, essayez 0.4plutôt, etc. jusqu'à ce que vous trouviez une valeur raisonnable. Cela vous donne également directement un paramètre de difficulté , que vous pouvez par exemple vouloir augmenter à mesure que le joueur avance dans le jeu.

sam hocevar
la source
Merci pour la réponse, mais cela ressemble à la réponse de l'ACEfanatic02.
Daniyal Azram
3

Un moyen simple de garantir que les lettres requises s'affichent dans un délai donné consiste à remplir un tableau avec les lettres du mot et le reste de l'alphabet (peut-être répété), puis de mélanger le tableau de manière aléatoire (c ++ a std :: random_shuffle dans la bibliothèque standard, si vous utilisez une langue différente, ce n'est pas difficile à implémenter).

Si vous souhaitez que les lettres du mot s'affichent plus rapidement, placez davantage de copies des mots-lettres dans le tableau.

GuyRT
la source
0

Si vous utilisez C ++, vous pouvez utiliser une distribution déjà existante http://en.cppreference.com/w/cpp/numeric/random/piecewise_constant_distribution

Il a également amorti la complexité à temps constant qui est meilleure que les méthodes naïves. exemple:

#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
la source