Shuffle Bags Haciendo al azar () Sentir más al azar

Un generador de números pseudoaleatorios (PRNG) como el Aleatorio La clase en C # no es un verdadero generador de números aleatorios: su objetivo es aproximar la aleatoriedad con la velocidad. Esto significa que a menudo devolverá una distribución desigual de valores, que puede que no sea lo que desea. En este tutorial, te mostraré cómo resolver este problema con un bolsa de viaje.

Nota: Aunque este tutorial usa C #, debería poder usar las mismas técnicas y conceptos en casi cualquier entorno de desarrollo de juegos.


Introducción

Cuando comencé a crear juegos, usé el estándar Aleatorio() Métodos para crear variedad en el juego, creando grandes si / else Condiciones hasta que recibí los resultados deseados. Si los resultados no estuvieran equilibrados como los quería, crearía condiciones adicionales hasta que sintiera que el juego era divertido. No fue hasta hace poco que me di cuenta de que existen mejores enfoques para crear una experiencia de juego realmente entretenida..

No hay nada de malo en el uso de la función incorporada. Aleatorio clase: el problema de no obtener los resultados deseados proviene de la implementación de los métodos. En este artículo usaremos el método de "Shuffle Bag" para hacer Aleatorio() Siéntete más aleatorio (y más divertido), utilizando los tableros Boggle como ejemplo práctico..


El problema

¿Alguna vez has notado que una expresión como:

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

... te da una distribución desigual de números?

A un generador de números aleatorios que selecciona valores entre 0 y 1 no le importa si devuelve todos los 1s, por lo que si ha creado un si / else bloquee usando la expresión anterior para elegir una rama, probablemente no esté obteniendo los resultados que espera.

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

No hay nada técnicamente mal con Aleatorio.Siguiente (), pero no garantiza una buena distribución uniforme de los números. Esto significa que en muchas situaciones de juego., Aleatorio() no es divertido.


¿Qué es una bolsa aleatoria??

Un Shuffle Bag es una técnica para controlar la aleatoriedad para crear la distribución que deseamos. La idea es:

  • Elija un rango de valores con la distribución deseada.
  • Pon todos estos valores en una bolsa..
  • Mezclar el contenido de la bolsa.
  • Tire de los valores uno por uno hasta que llegue al final.
  • Una vez que llega al final, comienza de nuevo, extrayendo los valores uno por uno nuevamente.

Implementación de una bolsa aleatoria

Implementar un Shuffle Bag en C # es simple, y la técnica se puede convertir fácilmente a cualquier idioma.

Como el propósito de este artículo es centrarse en la implementación de Shuffle Bags y no en las funciones de lenguaje, no analizaremos el uso de genéricos. Sin embargo, recomiendo encarecidamente el uso de genéricos porque nos permiten hacer estructuras de datos de tipo seguro sin comprometernos con los tipos de datos reales. Los genéricos le permiten usar el mismo código para crear Shuffle Bags que contienen muchos tipos diferentes de datos.

Aquí está el código básico:

 clase pública ShuffleBag private Random random = new Random (); Lista privada datos; privado char currentItem; private int currentPosition = -1; Private int Capacity get return data.Capacity;  public int Size get return data.Count;  público ShuffleBag (int initCapacity) data = new List (initCapacity); 

El comienzo de la clase configura las variables de instancia, y el constructor inicializa la variable de instancia de datos a la capacidad inicial del programador (es decir, qué tan grande es la bolsa para comenzar).

 Public void Add (elemento de carácter, cantidad int) para (int i = 0; i < amount; i++) data.Add(item); currentPosition = Size - 1; 

los Añadir método simplemente agrega el carbonizarse a datos tantas veces como el programador especifique.

Tenga en cuenta que el posición actual se establece al final de la lista, ya que recorreremos el final más adelante. ¿Por qué a partir del final de la lista? Podrías hacer que el Shuffle Bag se desplace desde el principio, pero comenzando por el final y trabajando hacia atrás hace que el código sea más limpio..

 public char 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; 

los Siguiente El método es la carne de esta técnica..

Si posición actual es menor que uno, lo restablecemos para que apunte al final de la lista y devolvamos el primer artículo de la bolsa. (Esto cubre la situación en la que hemos recorrido todos los elementos y ahora queremos comenzar de nuevo).

De lo contrario, utilizamos random.Next () para elegir un artículo al azar de la bolsa, desde algún lugar entre el primer artículo y el artículo en nuestra posición actual. Intercambiamos este artículo seleccionado al azar con el artículo en nuestra posición actual, y luego disminuimos posición actual por 1.

Finalmente, devolvemos el artículo seleccionado al azar. El resultado es que seguimos eligiendo artículos que no hemos elegido antes, mientras barajamos la bolsa a medida que avanzamos. Esto significa que su contenido está en un orden diferente cuando queremos recorrerlo de nuevo..

Ahora es el momento de probar nuestra nueva clase creada.


Usando la clase Shuffle Bag

Hace varios años creé un clon de Boggle para el iPhone..


Crédito de la imagen: Rich Brooks

Un problema que enfrenté fue crear tablas densas que solo usaban 16 letras, pero permitían al usuario formar cientos de palabras con esas 16 letras. Aprendí sobre las frecuencias de las letras y cómo podría usarlas para crear una experiencia de usuario positiva..

Al usar la frecuencia con la que aparecen las letras en el texto en inglés, podemos construir un diccionario ponderado.

 Diccionario estático privado letterFrequencies = nuevo diccionario '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

Nota: Q Se maneja un poco diferente a las otras letras. Retiene el valor de la tabla de frecuencia de letras, pero aparece como Qu en muchos juegos de palabras.

Ahora podemos crear una instancia de nuestra clase de Shuffle Bag, llenar nuestra Shuffle Bag con datos y crear tablas Boggle densas.

 estático vacío principal (cadena [] args) var shuffleBag = new ShuffleBag (88000); cantidad int = 0; foreach (var letter in letterFrequencies) cantidad = (int) letter.Value * 1000; shuffleBag.Add (letter.Key, amount);  para (int i = 0; i < 16; i++) Console.Write(shuffleBag.Next()); Console.WriteLine(); 

Nota: Lo más importante que se debe quitar de este código es la cantidad. Un multiplicador de 1000 devuelve mejores resultados que un multiplicador de 10..

Ejecutar los resultados a través de un solucionador en línea. Cuantas palabras encuentras?


Conclusión

En este artículo, reconocimos el problema en el uso de valores aleatorios con si / else En estas condiciones, introdujimos una solución utilizando Shuffle Bags y demostramos un uso mediante la creación de tablas Boggle densas. Con el uso de Shuffle Bags tomamos el control de Aleatorio() Métodos y crea una distribución uniforme de valores que ayudan a una experiencia de juego positiva..