web-dev-qa-db-fra.com

Besoin d'un générateur aléatoire prévisible

Je suis développeur de jeux Web et j'ai eu un problème avec les nombres aléatoires. Disons qu'un joueur a 20% de chances d'obtenir un coup critique avec son épée. Cela signifie que 1 accès sur 5 doit être critique. Le problème est que j'ai obtenu de très mauvais résultats dans la vie réelle - parfois les joueurs obtiennent 3 crits en 5 coups, parfois aucun en 15 coups. Les batailles sont plutôt courtes (3-10 coups), il est donc important d'obtenir une bonne distribution aléatoire.

Actuellement, j'utilise PHP mt_Rand(), mais nous déplaçons simplement notre code en C++, donc je veux résoudre ce problème dans le nouveau moteur de notre jeu.

Je ne sais pas si la solution est un générateur aléatoire uniforme, ou peut-être de se souvenir des états aléatoires précédents pour forcer une bonne distribution.

151
Thinker

Je suis d'accord avec les réponses précédentes selon lesquelles un véritable caractère aléatoire dans de petites séries de certains jeux n'est pas souhaitable - il semble trop injuste pour certains cas d'utilisation.

J'ai écrit un simple Shuffle Bag comme l'implémentation dans Ruby et j'ai fait quelques tests. L'implémentation a fait ceci:

  • Si cela semble toujours juste ou si nous n'avons pas atteint un seuil de rouleaux minimum, il retourne un bon coup basé sur la probabilité normale.
  • Si la probabilité observée à partir des jets antérieurs la rend injuste, elle renvoie un coup sûr.

Il est jugé injuste sur la base des probabilités aux limites. Par exemple, pour une probabilité de 20%, vous pouvez définir 10% comme borne inférieure et 40% comme borne supérieure.

En utilisant ces limites, j'ai trouvé qu'avec des séries de 10 hits, 14,2% du temps, la véritable implémentation pseudo-aléatoire produisait des résultats qui étaient hors de ces limites . Environ 11% du temps, aucun coup critique n'a été marqué en 10 essais. 3,3% du temps, 5 coups critiques ou plus ont été décrochés sur 10. Naturellement, en utilisant cet algorithme (avec un décompte minimum de 5), une quantité beaucoup plus petite (0,03%) des runs "Fairish" était hors limites . Même si l'implémentation ci-dessous ne convient pas (des choses plus intelligentes peuvent être faites, certainement), il convient de noter que, souvent, vos utilisateurs sentiront que c'est injuste avec une vraie solution pseudo-aléatoire.

Voici la viande de mon FairishBag écrite en Ruby. L'implémentation complète et la simulation rapide de Monte Carlo est disponible ici (Gist) .

def fire!
  hit = if @rolls >= @min_rolls && observed_probability > @unfair_high
    false
  elsif @rolls >= @min_rolls && observed_probability < @unfair_low
    true
  else
    Rand <= @probability
  end
  @hits += 1 if hit
  @rolls += 1
  return hit
end

def observed_probability
  @hits.to_f / @rolls
end

Mise à jour: L'utilisation de cette méthode augmente la probabilité globale d'obtenir un coup critique, à environ 22% en utilisant les limites ci-dessus. Vous pouvez compenser cela en définissant sa probabilité "réelle" un peu plus bas. Une probabilité de 17,5% avec la modification de foire donne une probabilité à long terme observée d'environ 20% et maintient les parcours à court terme se sentant passables.

39
Ian Terrell

Cela signifie que 1 accès sur 5 doit être critique. Le problème est que j'ai obtenu de très mauvais résultats dans la vie réelle - parfois les joueurs obtiennent 3 crits en 5 coups, parfois aucun en 15 coups.

Ce dont vous avez besoin est un shuffle bag . Cela résout le problème du vrai hasard trop aléatoire pour les jeux.

L'algorithme est à peu près comme ceci: vous mettez 1 coup critique et 4 coups non critiques dans un sac. Ensuite, vous randomisez leur commande dans le sac et les choisissez un à la fois. Lorsque le sac est vide, vous le remplissez à nouveau avec les mêmes valeurs et le randomisez. De cette façon, vous obtiendrez en moyenne 1 coup critique pour 5 coups, et au plus 2 coups critiques et 8 coups non critiques d'affilée. Augmentez le nombre d'articles dans le sac pour plus de hasard.

Voici un exemple de ne implémentation (en Java) et ses cas de test que j'ai écrit il y a quelque temps.

223
Esko Luontola

Vous avez une mauvaise compréhension de ce que signifie aléatoire.

Lequel est le plus aléatoire?

enter image description hereenter image description here

Alors que le deuxième tracé semble plus uniformément distribué, le plus aléatoire est en fait le premier tracé. L'esprit humain voit souvent des motifs dans le caractère aléatoire, donc nous voyons les amas du premier tracé comme des motifs, mais ils ne le sont pas - ils font simplement partie d'un échantillon sélectionné au hasard.

113
ceejayoz

Étant donné le comportement que vous demandez, je pense que vous randomisez la mauvaise variable.

Plutôt que de randomiser si ce coup sera critique, essayez de randomiser le nombre de tours jusqu'au prochain coup critique. Par exemple, choisissez simplement un nombre compris entre 2 et 9 chaque fois que le joueur obtient une critique, puis donnez-lui sa prochaine critique après que de nombreux tours se soient écoulés. Vous pouvez également utiliser des méthodes de dés pour vous rapprocher d'une distribution normale - par exemple, vous obtiendrez votre prochain coup critique en 2D4 tours.

Je crois que cette technique est également utilisée dans les RPG qui ont des rencontres aléatoires dans le monde extérieur - vous randomisez un compteur de pas, et après autant de pas, vous êtes de nouveau frappé. Cela semble beaucoup plus juste car vous ne vous faites presque jamais toucher par deux rencontres d'affilée - si cela se produit même une fois, les joueurs deviennent irritables.

88
AHelps

Tout d'abord, définissez une distribution "correcte". Les nombres aléatoires sont, eh bien, aléatoires - les résultats que vous voyez sont entièrement cohérents avec le (pseudo) hasard.

En développant cela, je suppose que ce que vous voulez, c'est un sentiment de "justice", donc l'utilisateur ne peut pas faire 100 tours sans succès. Si c'est le cas, je garderais une trace du nombre d'échecs depuis le dernier succès et je pondérerais le résultat généré. Supposons que vous vouliez que 1 lancer sur 5 réussisse. Donc, vous générez au hasard un nombre de 1 à 5, et si c'est 5, tant mieux.

Sinon, enregistrez l'échec, et la prochaine fois, générez un nombre de 1 à 5, mais ajoutez par exemple plancher (numFailures/2). Donc, cette fois encore, ils ont 1 chance sur 5. S'ils échouent, la prochaine fois, l'intervalle gagnant sera de 4 et 5; 2 chances sur 5 de succès. Avec ces choix, après 8 échecs, ils sont certains de réussir.

53
Adam Wright

Que diriez-vous de remplacer mt_Rand () par quelque chose comme ça?

XKCD comic (RFC 1149.5 specifies 4 as the standard IEEE-vetted random number.)

(RFC 1149.5 spécifie 4 comme le nombre aléatoire standard approuvé par l'IEEE.)

De XKCD .

35
Colin Pickard

J'espère que cet article vous aidera: http://web.archive.org/web/20090103063439/http://www.gamedev.net:80/reference/design/features/randomness/

Cette méthode de génération de "nombres aléatoires" est courante dans les jeux rpg/mmorpg.

Le problème qu'il résout est le suivant (extrait):

Une araignée-lame est à votre gorge. Il frappe et vous manquez. Il frappe à nouveau et vous manquez encore. Et encore et encore, jusqu'à ce qu'il ne vous reste plus rien à frapper. Vous êtes mort et un arachnide de deux tonnes jaillit sur votre cadavre. Impossible? Non. Improbable? Oui. Mais avec suffisamment de joueurs et suffisamment de temps, l'improbable devient presque certain. Ce n'était pas que l'araignée à lame était dure, c'était juste de la malchance. Quelle frustration. C'est suffisant pour donner envie à un joueur de quitter.

34
CiscoIPPhone

Ce que vous voulez, ce ne sont pas des nombres aléatoires, mais des nombres qui semblent aléatoires pour un humain. D'autres ont déjà suggéré des algorithmes individuels, qui peuvent vous aider, comme Shuffle Bad.

Pour une bonne analyse détaillée et approfondie de ce domaine, voir AI Game Programming Wisdom 2 . Tout le livre mérite d'être lu pour tout développeur de jeu, l'idée de "nombres apparemment aléatoires" est traitée dans le chapitre:

Aléatoire filtré pour les décisions d'IA et la logique de jeu :

Résumé: La sagesse conventionnelle suggère que meilleur est le générateur de nombres aléatoires, plus votre jeu sera imprévisible. Cependant, selon les études de psychologie, le véritable hasard à court terme semble souvent nettement aléatoire pour les humains. Cet article montre comment rendre les décisions aléatoires de l'IA et la logique du jeu plus aléatoires pour les joueurs, tout en conservant un fort caractère statistique aléatoire.

Vous pouvez également trouver un autre chapitre intéressant:

Les statistiques des nombres aléatoires

Résumé: Les nombres aléatoires sont les plus utilisés par l'intelligence artificielle et les jeux en général. Ignorer leur potentiel, c'est rendre le jeu prévisible et ennuyeux. Les utiliser de manière incorrecte peut être aussi mauvais que de les ignorer carrément. Comprendre comment les nombres aléatoires sont générés, leurs limites et leurs capacités, peut éliminer de nombreuses difficultés à les utiliser dans votre jeu. Cet article offre un aperçu des nombres aléatoires, de leur génération et des méthodes pour séparer les bons des mauvais.

19
Suma

Une génération de nombres aléatoires a-t-elle sûrement la chance de produire de telles séries? Vous n'obtiendrez pas un ensemble d'échantillons assez grand en 3-10 rouleaux pour voir les pourcentages appropriés.

Peut-être que ce que vous voulez, c'est un seuil de pitié ... rappelez-vous les 10 derniers rouleaux, et s'ils n'ont pas eu de coup critique, donnez-leur un cadeau. Lissez les frondes et les flèches de l'aléatoire.

8
James

Votre meilleure solution pourrait être de tester le jeu avec plusieurs schémas aléatoires différents non et de choisir celui qui rend les joueurs les plus heureux.

Vous pouvez également essayer une politique de sauvegarde pour le même nombre dans une rencontre donnée, par exemple, si un joueur lance un 1 au premier tour l'accepter. Pour obtenir un autre 1 ils doivent lancer 2 1s d'affilée. Pour obtenir un troisième 1 ils ont besoin de 3 d'affilée, à l'infini.

8
Hank Gay

Malheureusement, ce que vous demandez est en fait un générateur de nombres non aléatoires - parce que vous voulez que les résultats précédents soient pris en compte lors de la détermination du nombre suivant. Ce n'est pas ainsi que fonctionnent les générateurs de nombres aléatoires, je le crains.

Si vous voulez qu'un coup sur cinq soit un coup critique, choisissez simplement un nombre entre 1 et 5 et dites que ce coup sera un coup critique.

7
samjudson

mt_Rand () est basé sur une implémentation Mersenne Twister , ce qui signifie qu'il produit l'une des meilleures distributions aléatoires que vous pouvez obtenir.

Apparemment, ce que vous voulez n'est pas du tout aléatoire, vous devez donc commencer par spécifier exactement ce que vous voulez. Vous vous rendrez probablement compte que vous avez des attentes contradictoires - que les résultats devraient être vraiment aléatoires et non prévisibles, mais en même temps, ils ne devraient pas présenter de variations locales par rapport à la probabilité déclarée - mais alors cela devient prévisible. Si vous définissez un maximum de 10 non-crits d'affilée, alors vous venez de dire aux joueurs "si vous avez eu 9 non-crits d'affilée, la suivante sera critique avec 100% de certitude" - vous pourriez aussi ne vous embêtez pas du tout avec le hasard.

7

Je vois beaucoup de réponses suggérant de garder une trace des nombres générés précédemment ou de mélanger toutes les valeurs possibles.

Personnellement, je ne suis pas d'accord, que 3 crits d'affilée sont mauvais. Je ne suis pas non plus d'accord pour dire que 15 non-crits d'affilée sont mauvais.

Je résoudrais le problème, en modifiant la chance de critique elle-même, après chaque numéro. Exemple (pour démontrer l'idée):

int base_chance = 20;
int current_chance = base_chance;

int hit = generate_random_number(0, 100) + 1; // anything from 1 to 100
if(hit < current_chance)//Or whatever method you use to check
{
    //crit!
    if(current_chance > base_chance)
        current_chance = base_chance; // reset the chance.
    else
        current_chance *= 0.8; // decrease the crit chance for the NEXT hit.
}
else
{
    //no crit.
    if(current_chance < base_chance)
        current_chance = base_chance; // reset the chance.
    else
        current_chance *= 1.1; // increase the crit chance for the NEXT hit.
    //raise the current_chance
}

Plus vous n'obtenez pas de critique - plus vous avez de chances pour votre prochaine action de critiquer. La réinitialisation que j'ai incluse est entièrement facultative et nécessiterait des tests pour savoir si elle est nécessaire ou non. Il peut être souhaitable ou non de donner une probabilité plus élevée de critique pour plus d'une action consécutive, après une longue chaîne d'actions non critiques.

Jeter juste mes 2 cents ...

6
Paulius

Sur un si petit nombre de tests, vous devriez vous attendre à des résultats comme celui-ci:

Le véritable hasard n'est prévisible que sur une énorme taille de jeu, de sorte qu'il est tout à fait possible de lancer une pièce et d'obtenir des têtes 3 fois de suite, mais sur quelques millions de flips, vous vous retrouverez avec environ 50-50.

6
Ed James

Si vous voulez une distribution qui décourage les valeurs de répétition, vous pouvez utiliser un algorithme de rejet de répétition simple.

par exemple.

int GetRand(int nSize)
{
    return 1 + (::Rand() % nSize);
}
int GetDice()
{
    static int nPrevious=-1;
    while (1) {
        int nValue = GetRand(6);
        // only allow repeat 5% of the time
        if (nValue==nPrevious && GetRand(100)<95)
            continue;
        nPrevious = nValue;
        return nValue;
    }
}

Ce code rejette les valeurs de répétition 95% du temps, ce qui rend les répétitions improbables mais pas impossibles. Statistiquement, c'est un peu moche, mais cela produira probablement les résultats souhaités. Bien sûr, cela n'empêchera pas une distribution comme "5 4 5 4 5". Vous pourriez devenir plus chic et rejeter l'avant-dernier (par exemple) 60% du temps et l'avant-dernier (par exemple) 30%.

Je ne recommande pas cela comme une bonne conception de jeu. Suggérer simplement comment réaliser ce que vous voulez.

5
Michael J

Les premières réponses sont d'excellentes explications, je vais donc me concentrer uniquement sur un algorithme qui vous permet de contrôler la probabilité de "mauvaises séquences" tout en jamais devenant déterministe. Voici ce que je pense que vous devriez faire:

Au lieu de spécifier p, le paramètre d'une distribution de Bernoulli, qui est votre probabilité de coup critique, spécifiez a et b, les paramètres de la distribution bêta, le "conjugué a priori" de la distribution de Bernoulli. Vous devez garder une trace de A et B , le nombre de critiques et de non -des coups critiques jusqu'à présent.

Maintenant, pour spécifier a et b, assurez-vous que a/(a ​​+ b) = p, la chance d'un coup critique. La chose intéressante est que (a + b) quantifie à quelle distance vous voulez que l'A/(A + B) soit à p en général.

Vous faites votre échantillonnage comme ceci:

soit p(x) la fonction de densité de probabilité de la distribution bêta. Il est disponible dans de nombreux endroits, mais vous pouvez le trouver dans le GSL en tant que gsl_ran_beta_pdf.

S = A+B+1
p_1 = p((A+1)/S)
p_2 = p(A/S)

Choisissez un coup critique par échantillonnage à partir d'une distribution de Bernoulli avec probabilité p_1/(p_1 + p_2)

Si vous trouvez que les nombres aléatoires ont trop de "mauvaises séquences", augmentez a et b, mais dans la limite, comme a et b passez à l'infini, vous aurez l'approche du shuffle bag décrite précédemment.

Si vous implémentez cela, faites-moi savoir comment ça se passe!

5
Neil G

Eh bien, si vous êtes un peu en mathématiques, vous pouvez probablement essayer Distribution exponentielle

Par exemple, si lambda = 0,5, la valeur attendue est 2 (allez lire cet article!), Cela signifie que vous frapperez très probablement/crit/quoi que ce soit tous les 2 tours (comme 50%, hein?). Mais avec une telle distribution de probabilité, vous manquerez définitivement (ou ferez quoi que ce soit) au 0e tour (celui dans lequel l'événement s'était déjà produit et turn_counter avait été réinitialisé), vous avez 40% de chances de frapper le prochain tour, environ 65% chance de le faire 2ème (suivant après prochain) tour, environ 80% pour toucher le 3ème et ainsi de suite.

Le but de cette distribution est que si l'on a 50% de chances de toucher et qu'il manque 3 fois de suite, il va brusquement (enfin, plus de 80% de chances, et cela augmente à chaque tour suivant) touché. Cela conduit à des résultats plus "équitables", en gardant globalement 50% de chances inchangées.

En prenant vos 20% de chances de critiquer, vous avez

  • 17% de critiques au 1er tour
  • 32% pour critiquer le 2ème tour, si aucun critique ne se produit dans tous les précédents.
  • 45% pour critiquer le 3ème tour, si aucun critique ne se produit dans tous les précédents.
  • 54% pour critiquer le 4ème tour, si aucun critique ne se produit dans tous les précédents.
  • ...
  • 80% au 8e tour critique, si aucun critique ne se produit dans tous les précédents.

Sa probabilité d'environ 0,2% (contre 5%) est de 3 crits + 2 non-crits en 5 tours consécutifs. Et il y a 14% de chances de 4 non-crits conséquents, 5% de 5, 1,5% pour 6, 0,3% pour 7, 0,07% pour 8 non-crits conséquents. Je parie que c'est "plus juste" que 41%, 32%, 26%, 21% et 16%.

J'espère que vous ne vous ennuyez toujours pas à mort.

4
Dark

Ce que vous voulez n'est pas vraiment clair. Il est possible de créer une fonction telle que les 5 premières fois que vous l'appelez, elle retourne les nombres 1-5 dans un ordre aléatoire.

Mais ce n'est pas vraiment aléatoire. Le joueur saura qu'il obtiendra exactement un 5 lors des 5 prochaines attaques. C'est peut-être ce que vous voulez, et dans ce cas, vous n'avez qu'à le coder vous-même. (créez un tableau contenant les nombres, puis mélangez-les)

Alternativement, vous pouvez continuer à utiliser votre approche actuelle et supposer que vos résultats actuels sont dus à un mauvais générateur aléatoire. Notez que rien ne va peut-être mal avec vos numéros actuels. Les valeurs aléatoires sont aléatoires. parfois, vous obtenez 2, 3 ou 8 de la même valeur d'affilée. Parce qu'ils sont aléatoires. Un bon générateur aléatoire garantit simplement qu'en moyenne, tous les numéros seront retournés également souvent.

Bien sûr, si vous avez utilisé un mauvais générateur aléatoire, cela pourrait avoir faussé vos résultats, et si c'est le cas, le simple fait de passer à un meilleur générateur aléatoire devrait résoudre le problème. (Consultez la bibliothèque Boost.Random pour de meilleurs générateurs)

Alternativement, vous pouvez vous souvenir des N dernières valeurs renvoyées par votre fonction aléatoire et peser le résultat par celles-ci. (un exemple simple serait, "pour chaque occurrence du nouveau résultat, il y a 50% de chances que nous devrions éliminer la valeur et en obtenir une nouvelle"

Si je devais deviner, je dirais que s'en tenir au hasard "réel" est votre meilleur pari. Assurez-vous d'utiliser un bon générateur aléatoire, puis continuez comme vous le faites maintenant.

4
jalf

Je recommande un système de pourcentage progressif comme Blizzard utilise: http://www.shacknews.com/onearticle.x/57886

Généralement, vous lancez un RNG puis le comparez à une valeur pour déterminer si vous réussissez ou non. Cela peut ressembler à:

if ( randNumber <= .2 ) {
   //Critical
} else {
   //Normal
}

Tout ce que vous avez à faire est d'ajouter une augmentation progressive des chances de base ...

if (randNumber <= .2 + progressiveChance ) {
   progressiveChance = 0;
   //Critical
} else {
   progressiveChance += CHANCE_MODIFIER;
   //Normal hit
}

Si vous en avez besoin pour être plus sophistiqué, il est assez facile d'en ajouter plus. Vous pouvez limiter le montant que progressiveChance peut obtenir pour éviter une chance critique de 100% ou le réinitialiser sur certains événements. Vous pouvez également augmenter progressivement ProgressChance en petites quantités à chaque boost avec quelque chose comme progressiveChance + = (1 - progressiveChance) * SCALE où SCALE <1.

4
JonBWalsh

Vous pouvez créer une liste contenant les nombres de 1 à 5 et les faire trier par hasard. Parcourez ensuite la liste que vous avez créée. Vous avez la garantie de rencontrer chaque numéro au moins une fois ... Lorsque vous avez terminé avec les 5 premiers, créez simplement 5 autres numéros ...

4
Arjan Einbu

Qu'en est-il de faire dépendre les chances de critique des N dernières attaques? Un schéma simple est une sorte de chaîne de Markov: http://en.wikipedia.org/wiki/Markov_chain mais le code est quand même très simple.


IF turns_since_last_critical < M THEN 
   critial = false
   turns_since_last_critical++;
ELSE
   critial = IsCritical(chance);
   IF Critial THEN
       turns_since_last_critica = 0;
   ELSE
       turns_since_last_critica++;
   END IF;
END IF;

Bien sûr, vous devez faire vos calculs car la chance d'un critique est inférieure à la chance d'un critique une fois que vous savez qu'il y a eu suffisamment de tours depuis le dernier

3
borjab

OP,

À peu près, si vous voulez que ce soit juste, ce ne sera pas aléatoire.

Le problème de votre jeu est la durée réelle du match. Plus la correspondance est longue, moins vous allez voir le caractère aléatoire (le nombre de crits aura tendance à être de 20%) et cela va approcher vos valeurs prévues.

Vous avez deux options, pré-calculer les attaques en fonction des rouleaux précédents. Vous obtiendrez un critique toutes les 5 attaques (sur la base des vôtres de 20%), mais vous pouvez faire en sorte que cela se produise de manière aléatoire.

listOfFollowingAttacks = {Hit, Hit, Hit, Miss, Crit};

C'est le motif que vous voulez. Alors faites-le choisir au hasard dans cette liste, jusqu'à ce qu'elle soit vide, ils le recréent.

C'est un motif que j'ai créé pour mon jeu, il fonctionne assez bien, pour ce que je veux qu'il fasse.

votre deuxième option serait d'augmenter les chances de critiquer, vous allez probablement voir un nombre plus pair à la fin de toutes les attaques (en supposant que vos matchs se terminent assez rapidement). Moins vous avez de chances, plus vous obtenez de RNG.

2
Rodrigo

alt text

celui-ci est vraiment prévisible ... mais vous ne pouvez jamais en être sûr.

2

City of Heroes a en fait un mécanicien appelé le "coupe-bande" qui résout exactement ce problème. La façon dont cela fonctionne est qu'après une chaîne de ratés d'une longueur liée à la probabilité de toucher la plus faible de la chaîne, la prochaine attaque est garantie d'être un coup. Par exemple, si vous manquez une attaque avec plus de 90% de chances de coup, votre prochaine attaque sera automatiquement touchée, mais si vos chances de coup sont plus faibles, comme 60%, vous devrez avoir plusieurs échecs consécutifs pour déclencher le "coupe-bande" (I je ne connais pas les chiffres exacts)

2
Alex319

Vous regardez une distribution linéaire, alors que vous voulez probablement une distribution normale.

Si vous vous souvenez, dans votre jeunesse, jouer à D&D, on vous a demandé de lancer plusieurs dés à n faces, puis de faire la somme des résultats.

Par exemple, lancer un dé à 4 faces est différent de lancer un dé à 24 faces.

2
dar7yl

Pré-calculer un coup critique aléatoire pour chaque joueur.

// OBJECT
//...
// OnAttack()
//...
c_h = c_h -1;
if ( c_h == 0 ) {
 // Yes, critical hit!
 c_h = random(5) + 1 // for the next time
 // ...
}
0
Nick Dandoulakis

J'ai également essayé de résoudre ce problème. Le mieux que j'ai pu trouver était de changer dynamiquement les probabilités si les chiffres étaient basés sur la fréquence à laquelle ils étaient apparus dans le passé immédiat. Quelque chose comme (pour un dé, dans matlab):

probabilities = [1 1 1 1 1 1];
unrandomness = 1;
while true
    cumprob = cumsum(probabilities) ./ sum(probabilities);
    roll = find(cumprob >= Rand, 1)
    probabilities = probabilities + unrandomness;
    probabilities(roll) = probabilities(roll) - 6*unrandomness;
    if min(probabilities) < 0
        probabilities = probabilities - min(probabilities);
    end
end

Désolé pour le manque d'indentation. Le paramètre non aléatoire peut être ajusté comme vous le souhaitez. Véritable sortie aléatoire (non aléatoire = 0):

2 3 1 1 4 1 3 3 4 5 1 5 5 2 6 1 1 1 6 1 1 3 5 6 6 1 4 2 4 6 3 6 5 1 1 6 2 5 6 4 3 5 2 4 6 5 5 5 4 4 3 4 1 2

non aléatoire = 1:

3 2 4 5 6 2 6 2 4 1 5 5 1 6 4 3 1 4 2 1 3 2 6 5 3 6 5 3 1 4 1 6 5 3 4 2 1 6 5 4 1 4 3 1 6 6 5 4 3 1 5 2 3 2

Ça a l'air mieux je pense. Surtout si vous tracez les écarts entre les nombres.

0
Tim

Réaction sur: "Le problème est que j'ai eu de très mauvais résultats dans la vie réelle - parfois les joueurs obtiennent 3 crits en 5 coups, parfois aucun en 15 coups."

Vous avez une chance entre 3 et 4% de ne rien obtenir en 15 coups sûrs ...

0
Fortega

Que diriez-vous de pondérer la valeur?

Par exemple, si vous avez 20% de chances de réussir un coup critique, générez un nombre compris entre 1 et 5, un chiffre représentant un coup critique, ou un nombre compris entre 1 et 100, 20 nombres étant un coup critique.

Mais tant que vous travaillez avec des nombres aléatoires ou pseudo-aléatoires, il n'y a aucun moyen d'éviter potentiellement les résultats que vous voyez actuellement. C'est la nature du hasard.

0
Thomas Owens

Je pense que vous utilisez peut-être la mauvaise fonction de distribution aléatoire. Vous ne voulez probablement pas une distribution uniforme sur les chiffres. Essayez plutôt une distribution normale afin que les coups critiques deviennent plus rares que les coups "réguliers".

Je travaille avec Java donc je ne sais pas où vous pouvez trouver quelque chose pour C++ qui vous donne des nombres aléatoires avec une distribution normale mais il doit y avoir quelque chose là-bas.

0
Alex

Je pense que vous devez générer des nombres aléatoires à partir d'une distribution de Poisson.

0
Yogi

C'est IS comment ça devrait être, c'est la probabilité, vous ne devriez pas vous attendre à un coup critique 1ce toutes les 5 batailles .. essayez-le avec une pièce ou un dé et voyez par vous-même. Mais c'est juste moi . GB

0
Leo Jweda

Je proposerais le "dé de rebut différé au hasard" suivant:

  • Conservez deux tableaux, un (in-array) initialement rempli avec les valeurs de 0 à n-1, l'autre (out-array) vide
  • Lorsqu'un résultat est demandé:
    • retourne une valeur aléatoire parmi toutes les valeurs définies dans in-array
    • déplacer cette valeur de in-array à out-array
    • déplacer un élément aléatoire (sur tous, y compris l'élément non défini!) de out-array retour dans in-array

Cela a la propriété de "réagir" plus lentement lorsque le plus gros n est. Par exemple, si vous voulez une chance de 20%, mettre n à 5 et frapper sur un 0 est "moins aléatoire" que mettre n à 10 et frapper sur un 0 ou 1, et le faire de 0 à 199 sur 1000 sera presque impossible à distinguer du vrai hasard sur un petit échantillon. Vous devrez ajuster n à la taille de votre échantillon.

0
Svante

Comme beaucoup le disent, c'est vraiment un problème avec ce qui est "aléatoire". Les résultats que vous obtenez sont aléatoires et peu importe la façon dont vous créez le jeu, certains joueurs auront l'impression que votre compteur n'est pas juste et n'est pas aléatoire. ;)

Une option possible pourrait être de garantir un hit toutes les n fois et de générer n de façon aléatoire, dans certaines limites, après chaque hit. Tout dépend de ce qui "se sent" bien dans les tests de jeu.

0
mtrc