Sacs Shuffle Rendre le hasard () plus aléatoire

Un générateur de nombres pseudo aléatoires (PRNG) comme le au hasard La classe en C # n'est pas un véritable générateur de nombres aléatoires: son objectif est de rapprocher le hasard de la vitesse. Cela signifie que vous obtiendrez souvent une distribution inégale des valeurs, ce qui peut ne pas être ce que vous voulez. Dans ce tutoriel, je vais vous montrer comment résoudre ce problème avec un sac de shuffle.

Remarque: Bien que ce tutoriel utilise C #, vous devriez pouvoir utiliser les mêmes techniques et concepts dans presque tous les environnements de développement de jeux..


introduction

Quand j'ai commencé à créer des jeux, j'ai utilisé le standard Au hasard() méthodes pour créer de la variété dans le jeu, créant de grandes sinon conditions jusqu'à ce que j'ai reçu mes résultats souhaités. Si les résultats n'étaient pas équilibrés comme je le voulais, je créerais des conditions supplémentaires jusqu'à ce que je sente que le jeu est amusant. Ce n'est que récemment que j'ai réalisé qu'il existe de meilleures approches pour créer une expérience de jeu vraiment divertissante..

Il n'y a rien de mal avec l'utilisation de la fonction intégrée au hasard classe: le problème de ne pas obtenir les résultats souhaités provient de la mise en œuvre des méthodes. Dans cet article, nous allons utiliser la méthode "Shuffle Bag" pour créer Au hasard() se sentir plus aléatoire (et plus amusant), en utilisant les tableaux Boggle comme exemple pratique.


Le problème

Avez-vous déjà remarqué une expression comme:

 int value = Random.Next (lowerBounds, upperBounds);

… Vous donne une distribution inégale des nombres?

Un générateur de nombres aléatoires qui sélectionne des valeurs comprises entre 0 et 1 ne se soucie pas de renvoyer tous les 1, ainsi, si vous avez créé un sinon bloquer en utilisant l'expression ci-dessus pour choisir une branche, vous n'obtenez probablement pas les résultats escomptés.

 var rand = new Random (); pour (int i = 0; i < 10; i++) Console.WriteLine(rand.Next(0, 2));

Il n'y a rien techniquement faux avec Random.Next (), mais cela ne garantit pas une bonne distribution des nombres. Cela signifie que dans de nombreuses situations de jeu, Au hasard() n'est pas amusant.


Qu'est-ce qu'un sac de shuffle?

Le Shuffle Bag est une technique permettant de contrôler le caractère aléatoire afin de créer la distribution souhaitée. L'idée est:

  • Choisissez une plage de valeurs avec la distribution souhaitée.
  • Mettez toutes ces valeurs dans un sac.
  • Mélangez le contenu du sac.
  • Tirez les valeurs une par une jusqu'à la fin.
  • Une fois que vous avez atteint la fin, vous recommencez, en extrayant les valeurs une par une..

Mise en place d'un sac de lecture aléatoire

Implémenter un Shuffle Bag en C # est simple et la technique peut facilement être convertie en n'importe quelle langue..

Le but de cet article étant de se concentrer sur la mise en œuvre des sacs de shuffle et non sur les fonctionnalités du langage, nous n’examinerons pas l’utilisation des génériques. Cependant, je recommande fortement l'utilisation de génériques, car ils nous permettent de sécuriser les structures de données de type sans s'engager dans les types de données réels. Les génériques vous permettent d'utiliser le même code pour créer des sacs de lecture aléatoire contenant de nombreux types de données..

Voici le code de base:

 public class ShuffleBag private Random random = new Random (); liste privée Les données; private char currentItem; private int currentPosition = -1; private int Capacity get return data.Capacity;  public int Size get return data.Count;  public ShuffleBag (int initCapacity) data = new List (initCapacity); 

Le début de la classe configure les variables d’instance et le constructeur initialise la variable d’instance de données à la capacité initiale du programmeur (c’est-à-dire la taille de la poche pour commencer)..

 public void Add (élément de caractère, montant int) pour (int i = 0; i < amount; i++) data.Add(item); currentPosition = Size - 1; 

le Ajouter méthode ajoute simplement le carboniser à Les données autant de fois que le programmeur spécifie.

Notez que le position actuelle est placé à la fin de la liste, comme nous le ferons plus tard. Pourquoi de la fin de la liste? Vous pouvez faire en sorte que le Shuffle Bag traverse le début, mais commencer par la fin et revenir en arrière permettent d'obtenir un code plus propre..

 caractère public Next () if (currentPosition < 1)  currentPosition = Size - 1; currentItem = data[0]; return currentItem;  var pos = random.Next(currentPosition); currentItem = data[pos]; data[pos] = data[currentPosition]; data[currentPosition] = currentItem; currentPosition--; return currentItem; 

le Suivant méthode est la viande de cette technique.

Si position actuelle est inférieur à un, nous le réinitialisons pour qu'il pointe vers la fin de la liste et renvoie le premier article du sac. (Cela couvre la situation dans laquelle nous avons parcouru tous les éléments et souhaitons maintenant recommencer.)

Sinon, nous utilisons random.Next () choisir un article au hasard dans le sac, quelque part entre le premier article et l’article à notre position actuelle. Nous échangeons cet élément sélectionné au hasard avec celui de notre position actuelle, puis diminuons. position actuelle par 1.

Enfin, nous retournons l'élément sélectionné au hasard. Le résultat est que nous continuons à choisir des articles que nous n’avons pas choisis auparavant, tout en remuant le sac au fur et à mesure. Cela signifie que son contenu est dans un ordre différent lorsque nous voulons le parcourir à nouveau.

Il est maintenant temps d'essayer notre classe nouvellement créée.


Utilisation de la classe de sacs shuffle

Il y a plusieurs années, j'ai créé un clone Boggle pour iPhone.


Crédit image: Rich Brooks

Un problème auquel j’ai été confronté a été la création de tableaux denses ne contenant que 16 lettres, mais permettant à l’utilisateur de former des centaines de mots avec ces 16 lettres. J'ai découvert la fréquence des lettres et comment l'utiliser pour créer une expérience utilisateur positive.

En utilisant la fréquence à laquelle les lettres apparaissent dans le texte anglais, nous pouvons construire un dictionnaire pondéré.

 Dictionnaire statique privé letterFrequencies = nouveau dictionnaire 'E', 12,702, 'T', 9,056, 'A', 8.167, 'O', 7,507, 'I', 6,966, 'N', 6,769 , 'S', 6,327, 'H', 6,094, 'R', 5,987, 'D', 4.253, 'L', 4.025, 'C', 2.782, 'U', 2,758, 'M', 2,406, 'W', 2,306, 'F', 2.228, 'G', 2,015, 'Y', 1,974, ' P ', 1,929, ' B ', 1,492, ' V ', 0,978, ' K ', 0,772, ' J ', 0,153, ' X ', 0,150, ' Q ' , 0,095, 'Z', 0,074; // total: 99.965

Remarque: Q est traité un peu différemment des autres lettres. Il conserve la valeur du tableau de fréquence des lettres, mais il apparaît sous la forme Qu dans beaucoup de jeux de mots.

Maintenant, nous pouvons créer une instance de notre classe Shuffle Bag, remplir notre Shuffle Bag avec des données et créer des tableaux Boggle denses..

 static void Main (string [] args) var shuffleBag = new ShuffleBag (88000); int montant = 0; foreach (var lettre en letterFrequencies) montant = (int) lettre.Value * 1000; shuffleBag.Add (letter.Key, montant);  pour (int i = 0; i < 16; i++) Console.Write(shuffleBag.Next()); Console.WriteLine(); 

Remarque: La chose la plus importante à retenir de ce morceau de code est le montant. Un multiplicateur de 1000 renvoie de meilleurs résultats qu'un multiplicateur de 10.

Exécutez les résultats via un solutionneur en ligne. Combien de mots trouvez-vous?


Conclusion

Dans cet article, nous avons reconnu le problème de l’utilisation de valeurs aléatoires avec sinon Dans ces conditions, nous avons introduit une solution utilisant des sacs Shuffle et démontré une utilisation en créant des tableaux Boggle denses. Avec l’utilisation des sacs Shuffle, nous prenons le contrôle de Au hasard() méthodes et créer une distribution uniforme des valeurs qui contribuent à une expérience de jeu positive.