¿Cómo mejorar esta generación de números aleatorios en mi contexto?

11

En mi juego hay una palabra en la parte superior de la pantalla, las letras llueven desde arriba y el usuario tiene que tocar las letras para completar la palabra.

Actualmente estoy generando letras al azar (en realidad, los números aleatorios y los números son el índice para la matriz de letras. Por ejemplo: 0 = a, 1 = b) pero el problema es que lleva demasiado tiempo obtener todas las letras necesarias para completar el palabra.

Lo que quiero es que los números aleatorios que estoy generando generen las letras requeridas con mayor frecuencia para que el jugador no tenga que pasar todo el día para completar una palabra.

He intentado los siguientes métodos:

  1. Detecta todas las letras de la palabra (la palabra siempre tiene 6 letras de largo), genera la matriz de índices de longitud 6, asigna cada índice de la matriz a un número aleatorio de la letra 2 a la letra + 2 y al final elige aleatoriamente un índice de la matriz para mostrar.

  2. Tenga una variable de selector cuyo valor esté en el rango [0..2], generada aleatoriamente, si selector == 0 entonces detecte las letras que forman la palabra y elija aleatoriamente una letra, de lo contrario obtenga cualquier alfabeto al azar de az.

Ambos métodos no me han brindado ninguna ayuda. Estaré muy feliz si me pueden ayudar.

Gracias por leer esto, espero que haya entendido la pregunta y estoy esperando la respuesta.

Daniyal Azram
fuente
2
"Ambos métodos no me han brindado ninguna ayuda" ¿Por qué no? ¿Qué no funcionó con estos métodos?
Vaillancourt
No sé por qué, pero todavía me lleva demasiado tiempo, como 1 minuto, obtener todos los alfabetos necesarios.
Daniyal Azram
@DaniyalAzram probablemente deberías aumentar la frecuencia un poco más si estas letras no aparecen con la frecuencia suficiente, ya que parece que ese es tu problema.
JFA

Respuestas:

21

En realidad no quieres una distribución aleatoria. Lo señalo explícitamente, porque lo que consideramos "aleatorio" para el diseño generalmente no es aleatoriedad verdadera.

Ahora, con eso en mente, agreguemos algunos valores de ajuste: estas son cosas con las que jugará hasta que el diseño se sienta "correcto".

ChooseLetter() {
    const float WordLetterProbability = 0.5f;
    if (Random.NextFloat01() < WordLetterProbability) {
        // Pick a random letter in the word
    }
    else {
        // Pick a random letter *not* in the word
    }
}

La probabilidad controla la probabilidad de que una llamada dada a ChooseLetter le dé una letra de palabra: a 0.5, recibirá una letra de palabra aproximadamente cada dos veces. A 0.25, uno de cada cuatro será una letra de palabra, etc.

Esto sigue siendo un poco simplista, porque la aleatoriedad es, bueno, aleatoria , en realidad no tienes una garantía sobre cuánto tiempo pasarás entre las letras de las palabras. (En teoría, puedes ir para siempre sin una letra de palabra, es muy muy poco probable). En cambio, podemos agregar un factor para aumentar la probabilidad de una letra de palabra cada vez que no recibimos una:

const float BaseWordLetterProbability = 0.5f;
const float WordLetterProbabilityIncrease = 0.25f;
float WordLetterProbability = BaseWordLetterProbability;
ChooseLetter() {
    if (Random.NextFloat01() < WordLetterProbability) {
        // Pick a random letter in the word
        WordLetterProbability = BaseWordLetterProbability;
    }
    else {
        // Pick a random letter *not* in the word
        WordLetterProbability += WordLetterProbabilityIncrease;
    }
}

Bien, ahora nunca vamos más de dos letras sin una letra de palabra. (Porque, después de dos fallas, tenemos una probabilidad de 1.0 de obtener una letra de palabra).

Finalmente, debemos tener en cuenta cómo funciona elegir la letra de una palabra. Para proporcionar al jugador las letras que realmente necesita, necesitamos eliminar las letras del conjunto a medida que las recibe.

Por ejemplo, si la palabra es "prueba", y el jugador ya tiene 's', ¡no queremos darles otra 's' porque no la necesitan!

A partir de aquí, el resto se ajusta para adaptarse a su diseño.

ACEfanatic02
fuente
¡Guauu! ¿cómo se te ocurrió eso ?: p. Probaré tu método mañana por la mañana y creo que funcionará. Proporcionaré más comentarios una vez que pruebe este método. Muchas gracias por tal respuesta.
Daniyal Azram
44
¡Estadísticas! Ya sea como un diseñador de juegos o programador, las estadísticas son muy vale la pena aprender. Por lo menos, una comprensión de la probabilidad (y cómo combinarlas) es extremadamente útil.
ACEfanatic02
1
Ven a sugerir lo mismo. Gran respuesta. También tenga en cuenta que una solución como esta podría ser una forma de introducir niveles de dificultad. Fácil = 0.5, medio = 0.25, duro = 0.10. etc
Tasos
No estoy de acuerdo con que esto no sea aleatorio. Esto ES aleatorio, es solo una distribución por partes, mientras que generalmente pensamos en la distribución uniforme. Y para agregar a su idea, iría más allá de simplemente "Elija una letra aleatoria en la palabra", distribuiría eso por las letras que aparecen más. Por ejemplo, "mississippi" necesita más s e i, así que elige más.
Blaine
21

Puede ponderar la probabilidad de todas sus letras de acuerdo con la frecuencia con la que aparecen en el idioma en que se encuentran sus palabras. Una buena guía es el conjunto de scrabble . La versión en inglés, por ejemplo, tiene 12 E pero solo una Z y una Q.

Una manera simple de implementar esto es colocando todas las letras en una cadena consecutiva con cada letra apareciendo con la frecuencia deseada y luego haga que su RNG tome una letra de una posición aleatoria. Ejemplo de pseudocódigo:

const String letters = "AAAAAAAAABBCCDDDDEEEEEEEEEEEEFFGGGHHIIIIIIIIIJ/*...and so on...*/"

char randomLetter = letters[randomIntegerBetween(0, letters.length - 1)];
Philipp
fuente
2
+1 Este es un buen concepto, pero sospecho que hay una implementación más elegante.
Evorlor
Para una distribución más precisa, puede almacenar una tabla de frecuencias de letras escaladas para que sumen 1, generar un número aleatorio de 0 a 1 y recorrer la tabla restando la frecuencia de cada letra del número aleatorio hasta que se vuelve cero o negativo. Incluso puede optimizar esto ordenando las letras más comunes primero en la tabla. O use algo como el método de alias para evitar recorrer completamente la tabla.
Ilmari Karonen
44
Esto no resolvería el problema. El problema es que el usuario necesita letras específicas para terminar el juego. ¿Dicen que necesitan una Z? ¿Entonces que? Esto haría que las letras raras sean más difíciles de conseguir, lo que hará que el usuario se sienta aún más frustrado.
AmazingDreams
@AmazingDreams señala algo bueno, pero podemos modificarlo un poco, así que asignaré más peso a los alfabetos requeridos y menos peso a los demás. Debo decir que este es un muy buen concepto a seguir.
Daniyal Azram
4

Aquí hay una forma de mejorarlo usando un único parámetro kque puede ajustar.

En lugar de elegir una letra al azar:

  1. elige una letra al azar A
  2. elige un número aleatorio X
  3. si X > k y A no está [list of remaining needed letters], intente nuevamente en 1.

Cuanto más pequeña kes, más a menudo la letra final Aserá la que realmente se necesita.

Para ajustar el algoritmo, juegue con cualquier valor para k, por ejemplo k = 0.5 . Si encuentra que el juego es demasiado difícil, intente 0.4, etc., hasta que encuentre un valor razonable. Esto también le proporciona directamente una configuración de dificultad , que, por ejemplo, puede aumentar a medida que el jugador avanza en el juego.

sam hocevar
fuente
Gracias por la respuesta, pero esta parece ser la respuesta de ACEfanatic02.
Daniyal Azram
3

Una manera simple de garantizar que las letras requeridas se muestren dentro de un tiempo dado es usar llenar una matriz con las letras de la palabra y el resto del alfabeto (quizás repetido), luego barajar aleatoriamente la matriz (c ++ tiene std :: random_shuffle en la biblioteca estándar, si está utilizando un idioma diferente, no es difícil de implementar).

Si desea que las letras de la palabra aparezcan más rápidamente, coloque más copias de las letras de la palabra en la matriz.

GuyRT
fuente
0

Si está utilizando C ++, puede utilizar una distribución ya existente http://en.cppreference.com/w/cpp/numeric/random/piecewise_constant_distribution

También ha amortizado la complejidad del tiempo constante, que es mejor que los métodos ingenuos. ejemplo:

#include <iostream>
#include <string>
#include <map>
#include <random>
#include <numeric>

int main()
{
    constexpr double containedLetterWeight = 3.0;
    constexpr int iterations = 10000;
    std::string word = "DISTRIBUTION";

    std::mt19937 rng(123);

    std::vector<double> values('Z' - 'A' + 2);
    std::iota(values.begin(), values.end(), 0.0);

    std::vector<double> weights('Z' - 'A' + 1, 1.0);
    for(const auto& c : word) weights[c - 'A'] = containedLetterWeight;

    std::piecewise_constant_distribution<> dist(values.begin(), values.end(), weights.begin());

    std::map<char, int> results;
    for(int n = 0; n < iterations; ++n)
    {
        ++results[static_cast<char>(dist(rng)) + 'A'];
    }
    for(const auto& p : results)
    {
        std::cout << p.first << ' ' << static_cast<float>(p.second) / iterations << '\n';
    }
}
Sopel
fuente