¿Cómo produzco aleatoriamente "agradablemente", en lugar de pseudoaleatoria?

26

Estoy haciendo un juego que presenta varios tipos diferentes de rompecabezas en secuencia. Elijo cada rompecabezas con un número pseudoaleatorio. Para cada rompecabezas, hay una serie de variaciones. Elijo la variación con otro número pseudoaleatorio. Y así.

La cuestión es que, si bien esto produce una aleatoriedad casi verdadera, esto no es lo que el jugador realmente quiere. El jugador normalmente quiere lo que percibe e identifica como aleatorio, pero solo si no tiende a repetir acertijos. Entonces, no realmente al azar. Simplemente impredecible.

Al pensarlo un poco, me imagino formas extrañas de hacerlo. Por ejemplo, eliminar temporalmente las N opciones más recientes del conjunto de posibilidades al seleccionar una nueva opción. O asignando a cada opción una probabilidad igual, reduciendo la probabilidad de una opción a cero en la selección, y luego aumentando todas las probabilidades lentamente con cada selección.

Supongo que hay una forma establecida de hacer esto, pero simplemente no conozco la terminología, así que no puedo encontrarla. ¿Nadie sabe? ¿O alguien ha resuelto esto de una manera agradable?

Hilton Campbell
fuente
44
El libro "AI Game Programming Wisdom 2" tiene un capítulo sobre aleatoriedad filtrada que, por lo que recuerdo, es exactamente lo que estás buscando. Sin embargo, no lo tengo en este momento, así que realmente no puedo darte una respuesta completa.
Anton
Para tratar de aclarar: cuando dices 'no repetir rompecabezas', ¿quieres decir que simplemente no quieres dos rompecabezas del mismo tipo uno al lado del otro? En otras palabras, si acaba de elegir un sudoku, no ofrezca otro rompecabezas de sudoku, pero si fue Sudoku # 19, entonces está bien ofrecer Picross # 19 a continuación (en otras palabras, el número de variación no importa) ?
Steven Stadnicki
2
Pregunta muy relacionada: stackoverflow.com/questions/910215/…
Chris Burt-Brown
1
OK, mi copia de AI Game Programming Wisdom 2 acaba de llegar. Leí el capítulo sobre aleatoriedad filtrada y revisé el código fuente. Este es probablemente el mejor enfoque. Me permite usar números aleatorios, pero luego filtrar los números para que no ocurran patrones inesperados. Parece más a prueba de balas que la bolsa aleatoria.
Hilton Campbell
1
Otra actualización más ... para mi aplicación en particular, la aleatoriedad filtrada no lo hizo. Realmente quiero que el jugador juegue a través de todos los tipos y subtipos antes de repetir, así que fui con una baraja aleatoria.
Hilton Campbell

Respuestas:

25

Si tienes un número finito de rompecabezas, puedes:

  • Crea una lista de acertijos, todos ellos o algunos elegidos al azar;
  • Mezcle esta lista (vea Knuth Shuffle por ejemplo);
  • Deja que tu jugador juegue a través de esta lista;
  • Cuando la lista esté vacía, comience con una nueva.

EDITAR

No sabía esto, pero navegar por SE me hizo darme cuenta de que esto en realidad se conoce como una "bolsa aleatoria". Algunas informaciones más aquí , aquí o allá .

EDITAR 2

El clásico Knuth Shuffle dice lo siguiente:

To shuffle an array a of n elements (indices 0..n-1):
    for i from n  1 down to 1 do
        j  random integer with 0  j  i
        exchange a[j] and a[i]

Steven Stadnicki señaló acertadamente en su comentario que este tipo de cosas no evita la repetición en una reorganización. Una forma de tener esto en cuenta es agregar un caso especial para el último elemento:

To reshuffle an array a of n elements and prevent repetitions (indices 0..n-1):
    return if n <= 2

    // Classic Knuth Shuffle for all items *except* the last one
    for i from n  2 down to 1 do
        j  random integer with 0  j  i
        exchange a[j] and a[i]

    // Special case for the last item
    // Exchange it with an item which is *not* the first one
    r  random integer with 1  r  n - 1
    exchange a[r] and a[n - 1]
Laurent Couvidou
fuente
1
Esto puede funcionar, pero debes tener un poco de cuidado si vuelves a barajar después de cada partida para no comenzar con el mismo elemento en el que terminaste.
Steven Stadnicki
@ Steven de hecho. Este elemento podría excluirse de la nueva lista. En realidad, podría ser una idea crear una lista de algunos elementos aleatorios solamente, y crear la siguiente lista solo con los elementos restantes. Entonces, si tiene 100 elementos, cree, por ejemplo, una lista aleatoria de 10. Cuando haya terminado con esta lista, cree el siguiente con 10 elementos en los 90 que no se seleccionaron previamente.
Laurent Couvidou
+1. Se agregó soporte para que esta técnica sea "más divertida": así es como Tetris, por ejemplo, produce piezas "aleatorias". Se baraja e itera un conjunto de una de cada pieza, lo que evita las largas secuencias de duplicados que inevitablemente produciría la verdadera aleatoriedad.
KutuluMike
1
@Hilton Tiendo a disgustar este tipo de enfoque "mientras el ciclo hasta que el azar me da lo que quiero" ... No es muy probable que esto cause algún problema. Pero aún así, siempre siento que esta es una llamada para bucles infinitos aleatorios o caídas de rendimiento, que son terribles de depurar. Excluir el último elemento de la lista anterior de la nueva le permite barajar solo una vez, para obtener el mismo resultado.
Laurent Couvidou
1
Tienes razón, y yo tenía las mismas reservas. En lugar de excluir el último elemento anterior, ahora solo tengo que mezclarlo una vez, y luego, si el último elemento anterior es ahora el primero, lo cambio con algún otro elemento al azar.
Hilton Campbell
2

Una variante en el enfoque de lorancou: para cada tipo de rompecabezas, mantenga una serie de números de rompecabezas (barajados); luego, cada vez que juegues un rompecabezas de ese tipo, saca el siguiente número de la lista. por ejemplo, digamos que tienes rompecabezas Sudoku, Picross y Kenken, cada uno con los rompecabezas # 1..6. Crearía tres conjuntos aleatorios de los números 1..6, uno para cada tipo de rompecabezas:

  • Sudoku: [5, 6, 1, 3, 4, 2]
  • Picross: [6, 2, 4, 1, 3, 5]
  • KenKen: [3, 2, 5, 6, 4, 1]

Ahora, barajarías los tipos de rompecabezas como sugiere lorancu; digamos que aparece [Picross, Sudoku, Kenken]. Luego, cada vez que juegues un rompecabezas de un tipo dado, usa el siguiente número en su 'lista aleatoria'; en general, su presentación de rompecabezas sería [Sudoku # 5, Picross # 6, Kenken # 3, Sudoku # 6, Picross # 2, Kenken # 2, ...]

Si no desea mantener los acertijos en el mismo orden general cada vez a través del ciclo, entonces creo que su opción 'elegir al azar, ignorando las últimas selecciones' es la mejor. También hay maneras de hacer esto un poco más eficiente; por ejemplo, digamos que tienes 20 cosas y quieres ignorar las últimas 5 elegidas. Luego, en lugar de elegir aleatoriamente un número 1..20 y 'rebobinar' hasta obtener uno fuera de los últimos 5, en su lugar, elija un número 1..15 y recorra sus tipos de acertijos tantos pasos, simplemente salteando cualquier tipo de acertijo que sea elegido (puede hacer esto fácilmente manteniendo una matriz de bits que contenga los últimos 5 rompecabezas seleccionados).

Steven Stadnicki
fuente