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


151

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.


58
Il y a environ 0,5% de chances d'exactement 3 coups critiques et 2 non critiques et 3,5% de chances de 15 coups non critiques d'affilée, en supposant de vrais nombres aléatoires.
Nixuz

10
+1 au dessus. Une caractéristique des nombres aléatoires est que vous obtenez des valeurs aberrantes.
ConcernedOfTunbridgeWells

45
@Nixus: Non, c'est environ 5% de chances d'avoir 3 coups critiques et 2 non critiques, vous oubliez de multiplier par (5! / (3! * 2!)) = 10. Avec un niveau de confiance de 95%, c'est pas statistiquement improbable que 3 coups critiques se produisent en 5 coups.
erikkallen

7
Au début, je pensais que c'était une question idiote ... encore une fois, je suis honoré par SO.
SergioL

Réponses:


39

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:

  • Si cela semble toujours juste ou si nous n'avons pas atteint un seuil de jets minimum, cela renvoie un coup juste basé sur la probabilité normale.
  • Si la probabilité observée à partir des jets passés la rend inéquitable, elle renvoie un coup "équitable".

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) .

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


Je pense que c'est la meilleure solution qui répond à mes besoins. Le sac shuffle mentionné dans la meilleure réponse pointée n'est pas mauvais, mais nécessite beaucoup de calcul, et j'aime la solution la plus simple qui mène à la cible.
Thinker

Steve Rabin a écrit un article intéressant sur le caractère aléatoire des jeux. En bref, un véritable comportement «aléatoire» ne «semble» pas vraiment aléatoire pour la plupart des gens et des études l'ont confirmé. Son article s'intitule Filtered Randomness for AI Decisions and Game Logic et paraît dans "AI Game Programming Wisdom 2" (2003). Vous devriez le vérifier, il vous sera probablement utile.
Jeff Tucker

@IanTerrell Il serait bon d'indiquer la taille de l'échantillon de vos tests, c'est-à-dire le nombre de batailles pour déterminer ces probabilités.

@ user677656: C'est dans l'essentiel, mais c'est 100k
Ian Terrell

223

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.

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.


21
+ 1 pour bonne idée sans critique. Échelle du sac pour un degré plus élevé de hasard et pour gérer les écarts de chance critique entre les joueurs (si variable ofc)
TheMissingLINQ

16
Vous pourriez avoir une taille de sac de 10. Mettez en 1 coup, plus 30% de chance d'une seconde. Si la chance critique change, vous pouvez simplement jeter le sac et en créer un nouveau. Notez qu'un tel schéma court le risque que si vous (ou votre adversaire) connaissez votre probabilité de coup critique et la taille du sac, vous pouvez parfois être sûr que vous n'obtiendrez pas un autre critique pour un certain nombre de lancers. Cela pourrait affecter les tactiques.
Steve Jessop

3
Ouais ... dans quelque chose comme ça, vous exécutez des ricks similaires au comptage de cartes. Dois-je risquer un peon ou participer à la grande tuerie ... un petit ensemble fixe de résultats potentiels peut réduire le risque de perte et augmenter les chances d'être "joué"
Matthew Whited

8
J'aime l'idée du shuffle bag, mais je ne pense pas que cela corresponde à «l'esprit» du jeu car votre probabilité de coup critique de 20% (ce qui signifie que vous ne pouvez en faire aucun en 10 coups) n'est plus une probabilité. Cela devient exactement 1 coup tous les 5 coups. De plus, relancer le sac en cas de changement de probabilité de coup critique causera un problème dans le jeu. Si mon coup critique a été fait, je me jetterai le sort pour avoir le prochain critique plus tôt: p
Michaël Carpentier

2
@Jonathan: rendre le sac de la grande taille que vous lui donnez annule en fait toute l'idée du sac: s'assurer que quelque chose se passe (le critique qui est atteint) dans un nombre de lancers acceptable. Faire le sac 50000 grand équivaut à peu près à utiliser le générateur de nombres aléatoires.
dstibbe

113

Vous avez un malentendu sur ce que signifie aléatoire.

Lequel de ceux-ci est le plus aléatoire?

entrez la description de l'image ici entrez la description de l'image ici

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.


25
Bonne explication Numb3rs!
RMAAlmeida

9
Techniquement parlant, vous ne pouvez pas mesurer le caractère aléatoire. Les deux distributions me semblent assez arbitraires, même si je suppose que les deux ont été générées par algorithme. Vous pouvez effectuer un certain nombre de tests sur le premier graphique et déterminer qu'il est susceptible de provenir d'un processus qui place les points selon une distribution uniforme, mais vous ne pourrez pas conclure qu'il est plus aléatoire. En guise de contre-exemple, vous pouvez créer un tracé comme le premier en utilisant un générateur congruentiel linéaire, puis créer un tracé comme le second en utilisant le bruit amplifié d'une diode Zener. Essayez le mot non corrélé au lieu de aléatoire.
Dietrich Epp

8
Vous pouvez mesurer la probabilité qu'une dite distribution soit aléatoire.
ceejayoz


2
Pour être juste, alors qu'OP n'utilise peut-être pas les termes corrects, il comprend que le générateur de nombres aléatoires lui donne quelque chose de plus comme le premier graphique, quand il veut quelque chose de plus comme le deuxième graphique parce qu'il se sent plus "juste" pour l'utilisateur.
Kip

88

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.


Je pense que c'est une excellente solution, mais qu'en est-il de 80% de chance?
Penseur

J'aime aussi parfois utiliser des générateurs aléatoires multidimensionnels. Chance de toucher + Chance de puissance + Chance de critique. Tout comme lancer plusieurs dés différents dans D&D
Matthew Whited

J'aime cette idée, et vous avez tout à fait raison à propos du compteur de pas, qui est utilisé dans Final Fantasy depuis très longtemps, par exemple
Ed James

Un problème avec ceci est que cela ne fonctionne que si la probabilité d'un critique est à peu près constante entre les coups. Supposons qu'à mi-combat, le joueur lance un sort qui double sa probabilité de coup critique. Alors comment ajustez-vous le nombre de tours?
Alex319

+1 Très bien, gère également très facilement des probabilités de coup à coup inférieur à 20%.
Alice Purcell

53

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.


Sur cette note ... la plage de nombres aléatoires affecterait-elle la distribution ... par exemple en choisissant un Random r = new Random (); r.Next (1,5) contre r.Next (1, 1000000)% 200000
Eoin Campbell

23
+1 pour voir le problème derrière la demande au lieu de dire à l'OP qu'il comprend mal au hasard.
Boris Callens

4
Notez que si vous faites cela, leur proportion globale de succès sera supérieure à 1 sur 5. Un moyen de contourner ce problème consiste (par exemple) à choisir 20 nombres différents au hasard dans la plage 1 à 100, et à prédéterminer que ceux-ci seront être leurs critiques. C'est beaucoup plus de comptabilité, cependant.
Steve Jessop

"sera supérieur à 1 sur 5" - devrait être à long terme, je veux dire.
Steve Jessop

Vous pouvez réduire un peu la probabilité de départ, de sorte que la proportion globale soit réduite à 1/5. Je n'ai pas déterminé de combien vous devez le réduire, mais tout est continu donc il doit y avoir une bonne réponse.
Steve Jessop

35

Pourquoi ne pas remplacer mt_rand () par quelque chose comme ça?

Bande dessinée XKCD (RFC 1149.5 spécifie 4 comme nombre aléatoire standard approuvé par l'IEEE.)

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

De XKCD .


Bon, mais cela résoudra-t-il le problème de distribution des nombres aléatoires des OP? ;-)
Arjan Einbu

Pas vraiment; Il demande un RNG non aléatoire, pour lequel il pourrait utiliser cette fonction; mais ce qu'il veut vraiment est mieux expliqué par la meilleure réponse actuelle ( stackoverflow.com/questions/910215/910224#910224 )
Colin Pickard

28
C'est plus aléatoire que ne le veut l'OP
Çağdaş Tekin

-1 parce que RFC1149 n'a pas de section 5 (et alternativement, il n'y a pas 1149.5); +1 pour le juste hasard.
greyfade le

34

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):

Une araignée à lame est à votre gorge. Il frappe et vous manquez. Il frappe à nouveau et vous manquez à nouveau. Et encore et encore, jusqu'à ce qu'il ne reste plus rien de vous à frapper. Vous êtes mort et il y a un arachnide de deux tonnes jubilant 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. Comme c'est frustrant. Il suffit de donner envie à un joueur de quitter.


1
J'ai entendu une variante à ce sujet - "un événement sur un million se produit 6 000 fois dans la population mondiale".
ceejayoz

19

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.


8

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.


8

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, 1ils doivent lancer 2 1s d'affilée. Pour en obtenir un troisième, il 1leur en faut 3 d'affilée, à l'infini.


7

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.


1
il veut que le jeu soit aléatoire, si vous utilisez des nombres aléatoires stricts dans certains cas, vous finissez par avoir des résultats "aléatoires coréens". Ce sont des résultats aléatoires qui rendent les joueurs trop souvent en colère et frustrés (demandez à n'importe quel joueur de la lignée 2);)
Juan Techera

Par conséquent, si le premier coup est un critique, les quatre suivants ne le seront pas. Cela ressemble à ce que l'OP veut, mais quand vous le dites ainsi, cela semble retardé. Vous obtenez mon UV pour ça.
belgariontheking

-1 vous semblez confondre "aléatoire" avec "sans mémoire" - en.wikipedia.org/wiki/Memorylessness
Alice Purcell

7

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.


6

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.


7
Bien qu'il y ait encore une chance qu'après quelques millions de flips, vous n'ayez toujours vu qu'un seul côté de la pièce. Bien que si cela arrive, vous êtes probablement assis trop près d'un lecteur d'improbabilité infini: P
Grant Peters

haha, ouais, mais la chance est si incroyablement faible que les lois des mathématiques disent que vous devriez voir une distribution uniforme (ish).
Ed James

6

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):

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


J'aime ce genre d'approche. Je le ferais probablement dans l'autre sens. Commencez avec une chance moindre et augmentez jusqu'au maximum de 20% + un pourcentage supplémentaire jusqu'à ce qu'il atteigne et réinitialisez à nouveau à un faible montant.
Matthieu

5

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.

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

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!


5

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

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


Certaines valeurs de mon jeu comme les coups critiques ne peuvent pas avoir plus de 50% de chances, donc je bloque du tout la répétition, mais cela réduit les chances d'événement pour certains pourcentages.
Penseur

4

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.


En fait, la fonction qu'il utilise est la même que le meilleur RNG de la bibliothèque boost.
Michael Borgwardt

MT n'est pas le "meilleur", la dernière fois que j'ai vérifié. C'est sympa, simple et rapide, mais cela ne produit pas la meilleure distribution. Quoi qu'il en soit, prenez un million de nombres aléatoires et vérifiez la distribution. Découvrez si votre fonction aléatoire vous donne réellement une distribution uniforme ou non. Si ce n'est pas le cas, trouvez un meilleur générateur. Si c'est le cas, aspirez-le et acceptez la rangée occasionnelle de crits, ou trichez et rendez les résultats moins aléatoires et plus prévisibles.
jalf

4

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


4

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 à:

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

Tout ce que vous avez à faire est d'ajouter une augmentation progressive de la chance 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 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.


4

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

  • 17% aux critiques du 1er tour
  • 32% au deuxième tour critique, si aucun critique ne se produit dans tous les précédents.
  • 45% au troisième tour critique, si aucun critique ne se produit dans tous les tours précédents.
  • 54% au 4e tour critique, si aucun critique ne se produit dans tous les tours précédents.
  • ...
  • 80% au 8e tour critique, si aucun critique ne se produit dans tous les tours précédents.

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.


ceci est assez similaire à ma solution, sauf qu'il "se souvient" seulement du temps écoulé depuis le dernier coup critique. Dans le cadre de cette solution, une chaîne de 4 coups critiques équivaut à une chaîne de 1 coup critique en ce qui concerne les probabilités concernant l'avenir. Ainsi, si les coups critiques sont bons, cette solution limite votre risque de baisse, mais pas votre avantage. Ma solution affecte les deux.
Neil G

Il est évident que différentes solutions ont leurs propres avantages. Celui-ci se concentre sur le maintien de l'aléatoire propre du point de vue scientifique. Cela ne veut pas dire que c'est mieux que le sac ou quoi que ce soit d'autre. C'est juste une solution qui vaut la peine d'être essayée.
Dark

3

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.


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 que cela fait assez de tours depuis le dernier


Vous obtenez la plupart de l'effet en prenant simplement en compte la dernière attaque. Soit P le taux de réussite observé, R le taux de réussite après un échec et R / 2 le taux de réussite après un coup. Par définition, à tout moment, vous avez alors une chance d'atteindre P = P * R + (1-P) * (R / 2). Cela signifie P = R / (2-R)
MSalters

2

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.


2

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.


2

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)



0

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.


Et pourquoi cela ferait-il une différence? Il y a une chance exactement égale d'obtenir un critique pour les deux ensembles de nombres.
samjudson

Exactement. Je lui présente simplement deux options pour son exemple à 20%. Bien que 100 fonctionnerait probablement mieux si vous traitez avec des pourcentages de nombres entiers, car vous n'auriez besoin que de simuler un "dé", si vous voulez y penser comme ça.
Thomas Owens

Les options que vous présentez sont exactement ce qu'il fait déjà.
ceejayoz

2
Pas pour ce qu'il veut. Il veut un générateur de nombres non aléatoires, même s'il pense que cela s'appelle un générateur de nombres aléatoires.
ceejayoz

0

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


Lorsque vous avez 3500 joueurs en ligne combattant 10000 batailles en une minute, le problème qui se produit dans 3% des batailles est un problème très courant.
Penseur

Là encore, la malchance qui se produit dans 3% des batailles n'est toujours que de la malchance.
MSalters

0

Je proposerais la "matrice de remise différée au hasard" suivante:

  • Conservez deux tableaux, l'un ( in-array) initialement rempli avec les valeurs de 0 à n-1, l'autre ( out-array) vide
  • Lorsqu'un résultat est demandé:
    • renvoie une valeur aléatoire de toutes les valeurs définies dansin-array
    • déplacer cette valeur de in-arrayàout-array
    • déplacer un hasard (sur tous les éléments éléments, y compris le! indéfini) de out-arrayretour 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.


0

Précalculez 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

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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.