Je suis développeur de jeux Web et j'ai 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 qu'un résultat sur 5 devrait être critique. Le problème est que j'ai de très mauvais résultats dans la vraie vie - parfois les joueurs obtiennent 3 crédits en 5 coups, parfois aucun en 15 coups. Les batailles sont plutôt courtes (3-10 coups) donc il est important d'avoir une bonne distribution aléatoire.
Actuellement, j'utilise PHP mt_rand()
, mais nous ne faisons que déplacer notre code vers 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 pour se souvenir des états aléatoires précédents pour forcer une distribution appropriée.
Réponses:
Je suis d'accord avec les réponses précédentes selon lesquelles le vrai hasard dans de petites séries de certains jeux n'est pas souhaitable - cela 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. La mise en œuvre a fait ceci:
Il est jugé injuste sur la base des probabilités limites. Par exemple, pour une probabilité de 20%, vous pouvez définir 10% comme limite inférieure et 40% comme limite supérieure.
En utilisant ces limites, j'ai constaté qu'avec des séries de 10 résultats, 14,2% du temps, la véritable mise en œuvre pseudo-aléatoire produisait des résultats qui étaient hors de ces limites . Environ 11% du temps, 0 coup critique a été marqué en 10 essais. 3,3% du temps, 5 coups critiques ou plus ont été obtenus sur 10. Naturellement, en utilisant cet algorithme (avec un nombre de lancers minimum de 5), une quantité beaucoup plus petite (0,03%) des exécutions "Fairish" étaient hors limites . Même si l'implémentation ci-dessous n'est pas appropriée (des choses plus intelligentes peuvent être faites, certainement), il convient de noter que vos utilisateurs ressentiront souvent que c'est injuste avec une vraie solution pseudo-aléatoire.
Voici la viande de mon
FairishBag
écrit en rubis. La mise en œuvre complète et la simulation rapide de Monte Carlo sont disponibles ici (gist) .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 basse. Une probabilité de 17,5% avec la modification fairish donne une probabilité observée à long terme d'environ 20% et maintient les courses à court terme équitables.
la source
Ce dont vous avez besoin est un sac shuffle . Cela résout le problème du vrai hasard étant 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 choisissez-les une par une. 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'objets dans le sac pour plus d'aléatoire.
Voici un exemple d' implémentation (en Java) et ses cas de test que j'ai écrit il y a quelque temps.
la source
Vous avez un malentendu sur ce que signifie aléatoire.
Lequel de ceux-ci est le plus aléatoire?
Alors que la deuxième parcelle semble plus uniformément répartie, plus la première parcelle est aléatoire . L'esprit humain voit souvent des modèles de manière aléatoire, nous voyons donc les amas dans le premier graphique comme des modèles, mais ils ne le sont pas - ils font simplement partie d'un échantillon sélectionné au hasard.
la source
Compte tenu du comportement que vous demandez, je pense que vous randomisez la mauvaise variable.
Plutôt que de déterminer au hasard si ce coup sera critique, essayez de randomiser le nombre de tours jusqu'à ce que le prochain coup critique se produise. Par exemple, choisissez simplement un nombre entre 2 et 9 chaque fois que le joueur reçoit un critique, puis donnez-lui son prochain 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 critique dans les tours 2D4.
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 ces nombreuses étapes, vous êtes à nouveau touché. Cela semble beaucoup plus juste parce que vous n'êtes presque jamais touché par deux rencontres consécutives - si cela se produit une seule fois, les joueurs deviennent irritables.
la source
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 compatibles avec le (pseudo) hasard.
En développant cela, je suppose que ce que vous voulez, c'est un sentiment d '«équité», de sorte que l'utilisateur ne peut pas faire 100 tours sans succès. Si tel 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 qu'un jet sur 5 "réussisse". Donc, vous générez au hasard un nombre de 1 à 5, et si c'est 5, c'est parfait.
Sinon, enregistrez l'échec, et la prochaine fois, générez un nombre de 1 à 5, mais ajoutez par exemple, floor (numFailures / 2). Donc, cette fois encore, ils ont 1 chance sur 5. S'ils échouent, la prochaine fois, l'intervalle gagnant est de 4 et 5; une chance de succès de 2 sur 5. Avec ces choix, après 8 échecs, ils sont certains de réussir.
la source
Pourquoi ne pas remplacer mt_rand () par quelque chose comme ça?
(RFC 1149.5 spécifie 4 comme nombre aléatoire standard approuvé par l'IEEE.)
De XKCD .
la source
Espérons 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):
la source
Ce que vous voulez, ce ne sont pas des nombres aléatoires, mais des nombres qui semblent aléatoires à 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 . Le livre entier vaut la peine d'être lu pour tout développeur de jeux, l'idée de "nombres apparemment aléatoires" est traitée dans le chapitre:
Aléatoire filtré pour les décisions AI et la logique de jeu :
Résumé: La sagesse conventionnelle suggère que plus le générateur de nombres aléatoires est performant, plus votre jeu sera imprévisible. Cependant, selon des études de psychologie, le vrai hasard à court terme semble souvent incontrôlable 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 aléatoire statistique.
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.
la source
N'importe quelle génération de nombres aléatoires a-t-elle la chance de produire de telles séries? Vous n'obtiendrez pas un ensemble d'échantillons suffisamment grand en 3 à 10 rouleaux pour voir les pourcentages appropriés.
Peut-être que ce que vous voulez, c'est un seuil de miséricorde ... souvenez-vous des 10 derniers jets, et s'ils n'ont pas eu de coup critique, donnez-leur un cadeau. Lissez les élingues et les flèches du hasard.
la source
Votre meilleure solution pourrait être de tester le jeu avec plusieurs schémas différents non aléatoires et de choisir celui qui rend les joueurs les plus heureux.
Vous pouvez également essayer une politique de retrait pour le même numéro dans une rencontre donnée, par exemple, si un joueur obtient un
1
à son premier tour, acceptez-le. Pour en obtenir un autre,1
ils doivent lancer 21
s d'affilée. Pour en obtenir un troisième, il1
leur en faut 3 d'affilée, à l'infini.la source
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, j'en ai peur.
Si vous voulez qu'un coup sur 5 soit un coup critique, choisissez simplement un nombre entre 1 et 5 et dites que ce coup sera un coup critique.
la source
mt_rand () est basé sur une implémentation de Mersenne Twister , ce qui signifie qu'il produit l'une des meilleures distributions aléatoires que vous puissiez 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 doivent être vraiment aléatoires et non prévisibles, mais en même temps, ils ne doivent pas présenter de variations locales par rapport à la probabilité déclarée - mais cela devient alors 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, le prochain sera critique avec 100% de certitude" - vous pourriez aussi bien pas du tout dérangé par le hasard.
la source
Sur un si petit nombre de tests, vous devriez vous attendre à des résultats comme celui-ci:
Le vrai hasard n'est prévisible que sur une taille de jeu énorme, de sorte qu'il est tout à fait possible de lancer une pièce et d'obtenir des têtes 3 fois de suite pour la première fois, mais sur quelques millions de flips, vous obtiendrez environ 50-50.
la source
Je vois beaucoup de réponses suggérant de garder une trace des nombres précédemment générés ou de mélanger toutes les valeurs possibles.
Personnellement, je ne suis pas d'accord, que 3 crits d'affilée sont mauvais. Je suis non plus d'accord que 15 non-crits d'affilée sont mauvais.
Je résoudrais le problème, en modifiant la chance critique elle-même, après chaque numéro. Exemple (pour démontrer l'idée):
Plus vous n'obtenez pas de critique, plus vous avez de chances que votre prochaine action soit critique. 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 d'un critique pour plus d'une action consécutive, après une longue chaîne d'actions non critiques.
Juste jeter mes 2 cents ...
la source
Les quelques réponses les plus importantes sont d'excellentes explications, je vais donc me concentrer uniquement sur un algorithme qui vous permet de contrôler la probabilité de «mauvaises stries» sans jamais devenir 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é d'un coup critique, spécifiez a et b , les paramètres de la distribution bêta, le "a priori conjugué" de la distribution de Bernoulli. Vous devez garder une trace de A et B , le nombre de coups critiques et non 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 à quel point vous voulez que le A / (A + B) soit proche de p en général.
Vous faites votre échantillonnage comme ceci:
laissez -
p(x)
être 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 sous le nom gsl_ran_beta_pdf.Choisissez un coup critique en échantillonnant à partir d'une distribution de bernoulli avec une probabilité p_1 / (p_1 + p_2)
Si vous trouvez que les nombres aléatoires ont trop de "mauvaises stries", augmentez a et b , mais dans la limite, comme a et b vont à l'infini, vous aurez l'approche de shuffle bag décrite précédemment.
Si vous implémentez cela, dites-moi comment cela se passe!
la source
Si vous voulez une distribution qui décourage les valeurs de répétition, vous pouvez utiliser un simple algorithme de rejet de répétition.
par exemple
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 que vous souhaitez. Bien sûr, cela n'empêchera pas une distribution comme "5 4 5 4 5". Vous pourriez devenir plus sophistiqué et rejeter l'avant-dernier (disons) 60% du temps et le troisième dernier (disons) 30%.
Je ne recommande pas cela comme une bonne conception de jeu. Suggérant simplement comment réaliser ce que vous voulez.
la source
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 renvoie 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 devez simplement le coder vous-même. (créer un tableau contenant les nombres puis les mélanger)
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 pas 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 nombres seront renvoyé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)
Vous pouvez également 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 devions ignorer 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.
la source
Vous pouvez créer une liste contenant les nombres de 1 à 5 et les classer par hasard. Ensuite, parcourez simplement 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 ...
la source
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 comparez-le à une valeur pour déterminer s'il réussit ou non. Cela peut ressembler à:
Tout ce que vous avez à faire est d'ajouter une augmentation progressive de la chance de base ...
Si vous en avez besoin pour être plus sophistiqué, il est assez facile d'en ajouter plus. Vous pouvez plafonner le montant que progressiveChance peut obtenir pour éviter une chance critique de 100% ou le réinitialiser sur certains événements. Vous pouvez également avoir une augmentation progressiveChance de plus petites quantités à chaque boost avec quelque chose comme progressChance + = (1 - progressiveChance) * SCALE où SCALE <1.
la source
Eh bien, si vous êtes un peu en maths, vous pouvez probablement essayer la distribution exponentielle
Par exemple, si lambda = 0.5, la valeur attendue est 2 (allez lire cet article!), Cela signifie que vous frapperez probablement / crit / peu importe tous les 2 tours (comme 50%, hein?). Mais avec une telle distribution de probabilité, vous manquerez définitivement (ou ferez le contraire) au 0e tour (celui dans lequel l'événement s'était déjà produit et turn_counter avait été réinitialisé), aurez 40% de chances de frapper le prochain tour, environ 65% chance de le faire 2ème (suivant après suivant) tour, environ 80% pour frapper 3ème et ainsi de suite.
Le but de cette distribution est que si quelqu'un a 50% de chances de toucher et qu'il rate 3 fois de suite, il réussira timidement (enfin, plus de 80% de chances, et cela augmente à chaque tour suivant). Cela conduit à des résultats plus «équitables», en gardant inchangés les chances de 50%.
En prenant vos 20% de chances de critique, vous avez
C'est toujours environ 0,2% (contre ces 5%) de chances de 3 crits + 2 non-crits en 5 tours consécutifs. Et il y a 14% de chance 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.
la source
Pourquoi ne pas faire dépendre les chances de critique des N dernières attaques? Un schéma simple est une sorte de chaîne markov: http://en.wikipedia.org/wiki/Markov_chain mais le code est de toute façon très simple.
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 que cela fait assez de tours depuis le dernier
la source
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 verrez d'aléatoire (les crits auront tendance à être de 20%) et il se rapprochera des valeurs souhaitées.
Vous avez deux options, pré-calculez les attaques en fonction des lancers précédents. Vous obtiendrez un critique toutes les 5 attaques (basé sur le vôtre 20%), mais vous pouvez rendre l'ordre dans lequel il se produit au hasard.
listOfFollowingAttacks = {Hit, Hit, Hit, Miss, Crit};
C'est le modèle que vous voulez. Alors faites-le choisir au hasard dans cette liste, jusqu'à ce qu'elle soit vide, ils la recréent.
C'est un modèle que j'ai créé pour mon jeu, cela fonctionne assez bien, pour ce que je veux qu'il fasse.
votre deuxième option, serait d'augmenter les chances de critique, vous allez probablement voir un nombre plus pair à la fin de toutes les attaques (en supposant que vos matchs se terminent assez rapidement). Moins il y a de chances, plus vous obtenez de RNG.
la source
Vous regardez une distribution linéaire, alors que vous voulez probablement une distribution normale.
Si vous vous souvenez de votre jeunesse en jouant à 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é de 4 x 6 faces est différent de lancer 1 x de dés à 24 faces.
la source
City of Heroes a en fait un mécanisme appelé le "briseur de séquence" 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 plus faible probabilité de toucher dans la chaîne, la prochaine attaque est garantie d'être un succès. Par exemple, si vous manquez une attaque avec plus de 90% de chances de toucher, votre prochaine attaque frappera automatiquement, mais si votre chance de toucher est inférieure à 60%, vous devrez avoir plusieurs échecs consécutifs pour déclencher le "briseur de séquence" (je je ne connais pas les nombres exacts)
la source
celui-ci est vraiment prévisible ... mais on ne peut jamais en être sûr.
la source
Que diriez-vous de pondérer la valeur?
Par exemple, si vous avez 20% de chances de subir un coup critique, générez un nombre entre 1 et 5 avec un nombre représentant un coup critique, ou un nombre entre 1 et 100 avec 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.
la source
Réaction sur: "Le problème est que j'ai de très mauvais résultats dans la vie réelle - parfois les joueurs obtiennent 3 crédits en 5 coups, parfois aucun en 15 coups."
Vous avez une chance entre 3 et 4% de ne rien obtenir en 15 coups ...
la source
Je proposerais la "matrice de remise différée au hasard" suivante:
in-array
) initialement rempli avec les valeurs de 0 à n-1, l'autre (out-array
) videin-array
in-array
àout-array
out-array
retour enin-array
Cela a la propriété de "réagir" plus lentement plus n est grand. Par exemple, si vous voulez une chance de 20%, mettre n à 5 et frapper un 0 est "moins aléatoire" que régler n à 10 et frapper un 0 ou 1, et faire de 0 à 199 sur 1000 sera presque indiscernable du vrai hasard sur un petit échantillon. Vous devrez ajuster n à la taille de votre échantillon.
la source
Précalculez un coup critique aléatoire pour chaque joueur.
la source
Je pense que vous utilisez peut-être la mauvaise fonction de distribution aléatoire. Vous ne voulez probablement pas une répartition uniforme des chiffres. Essayez plutôt une distribution normale afin que les coups critiques deviennent plus rares que les coups «normaux».
Je travaille avec Java, donc je ne sais pas où 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.
la source