Comment puis-je produire «agréablement» au hasard, par opposition au pseudo-aléatoire?

26

Je fais un jeu qui présente plusieurs types de puzzles en séquence. Je choisis chaque puzzle avec un numéro pseudo-aléatoire. Pour chaque puzzle, il existe un certain nombre de variantes. Je choisis la variation avec un autre numéro pseudo-aléatoire. Etc.

Le fait est que, même si cela produit un hasard presque vrai, ce n'est pas vraiment ce que le joueur veut. Le joueur veut généralement ce qu'il perçoit et s'identifie comme aléatoire, mais seulement s'il n'a pas tendance à répéter les puzzles. Donc, pas vraiment aléatoire. Juste imprévisible.

En y réfléchissant, je peux imaginer des façons hacky de le faire. Par exemple, éliminer temporairement les choix N les plus récents de l'ensemble des possibilités lors de la sélection d'un nouveau choix. Ou attribuer à chaque choix une probabilité égale, réduire la probabilité d'un choix à zéro lors de la sélection, puis augmenter lentement toutes les probabilités à chaque sélection.

Je suppose qu'il existe une façon établie de le faire, mais je ne connais pas la terminologie, donc je ne la trouve pas. Quelqu'un sait? Ou quelqu'un a-t-il résolu cela d'une manière agréable?

Hilton Campbell
la source
4
Le livre "AI Game Programming Wisdom 2" a un chapitre sur le hasard filtré qui, d'après ce que je me souviens, est à peu près exactement ce que vous recherchez. Je ne l'ai pas pour le moment, donc je ne peux pas vraiment vous donner une réponse complète.
Anton
Pour essayer de clarifier: lorsque vous dites «ne pas répéter les puzzles», voulez-vous dire que vous ne voulez tout simplement pas deux puzzles du même type l'un à côté de l'autre? Donc, en d'autres termes, si vous venez de choisir un sudoku, ne proposez pas un autre puzzle de sudoku, mais s'il s'agissait du Sudoku # 19, alors vous pouvez proposer Picross # 19 ensuite (en d'autres termes, le numéro de variation n'a pas d'importance) ?
Steven Stadnicki,
2
Question très connexe: stackoverflow.com/questions/910215/…
Chris Burt-Brown
1
OK, ma copie de AI Game Programming Wisdom 2 vient d'arriver. J'ai lu le chapitre sur l'aléatoire filtré et vérifié le code source. C'est probablement la meilleure approche. Cela me permet d'utiliser simplement des nombres aléatoires, mais de filtrer ensuite les nombres de sorte que les modèles inattendus ne se produisent pas. Il semble plus à l'épreuve des balles que le sac à main.
Hilton Campbell
1
Encore une autre mise à jour ... pour mon application particulière, l'aléatoire filtré ne l'a pas tout à fait fait. Je veux vraiment que le joueur joue à travers tous les types et sous-types avant de répéter, alors je suis allé avec un sac à main.
Hilton Campbell

Réponses:

25

Si vous avez un nombre fini de puzzles, vous pouvez:

  • Construisez une liste de puzzles, tous ou certains choisis au hasard;
  • Mélangez cette liste (voir le mélange de Knuth par exemple);
  • Laissez votre joueur parcourir cette liste;
  • Lorsque la liste est vide, commencez par une nouvelle.

MODIFIER

Je ne le savais pas, mais en parcourant SE, je me suis rendu compte que cela était en fait connu sous le nom de "shuffle bag". Plus d'infos ici , ici ou .

EDIT 2

Le classique Knuth Shuffle va dans ce sens:

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 a souligné à juste titre dans son commentaire que ce genre de chose n'empêche pas la répétition lors d'un remaniement. Un moyen de prendre cela en compte est d'ajouter un cas spécial pour le dernier article:

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
la source
1
Cela peut fonctionner mais vous devez être un peu prudent si vous mélangez après chaque lecture pour ne pas commencer avec le même élément que vous avez terminé.
Steven Stadnicki
@Steven Effectivement. Cet élément pourrait être exclu de la nouvelle liste. En fait, cela pourrait être une idée de construire une liste de quelques éléments aléatoires uniquement, et de créer la liste suivante avec uniquement les éléments restants. Donc, si vous avez 100 éléments, construisez par exemple une liste mélangée de 10. Lorsque vous avez terminé avec cette liste, créez la suivante avec 10 éléments parmi les 90 qui n'ont pas été choisis auparavant.
Laurent Couvidou
+1. Ajout du support pour que cette technique soit "plus amusante": c'est ainsi que Tetris, par exemple, produit des pièces "aléatoires". Un ensemble d'une de chaque pièce est mélangé et réitéré, ce qui évite les longues séquences de doublons que le véritable hasard produirait inévitablement.
KutuluMike
1
@Hilton, j'ai tendance à ne pas aimer ce genre d'approche "en boucle jusqu'à ce que le hasard me donne ce que je veux" ... Il est peu probable que cela cause des problèmes. Mais toujours, j'ai toujours l'impression que c'est un appel à des boucles infinies aléatoires ou à des baisses de performances - qui sont horribles à déboguer. L'exclusion du dernier élément de la liste précédente de la nouvelle vous permet de mélanger une seule fois, pour le même résultat.
Laurent Couvidou
1
Vous avez raison, et j'avais les mêmes réserves. Plutôt que d'exclure le dernier élément précédent, je l'ai maintenant simplement mélangé une fois, puis si le dernier élément précédent est maintenant le premier, je l'échange avec un autre élément au hasard.
Hilton Campbell
2

Une variante de l'approche de Lorancou: pour chaque type de puzzle, conservez un tableau de numéros de puzzle (mélangés); puis chaque fois que vous frappez un puzzle de ce type, obtenez le numéro suivant de la liste. par exemple, supposons que vous ayez des puzzles Sudoku, Picross et Kenken, chacun avec des puzzles # 1..6. Vous créeriez trois tableaux mélangés des nombres 1 à 6, un pour chaque type de puzzle:

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

Maintenant, vous devez mélanger les types de puzzles comme le suggère Lorancu; disons que cela arrive [Picross, Sudoku, Kenken]. Ensuite, chaque fois que vous touchez un puzzle d'un type donné, utilisez le numéro suivant dans sa «liste aléatoire»; dans l'ensemble, votre présentation de puzzle serait [Sudoku # 5, Picross # 6, Kenken # 3, Sudoku # 6, Picross # 2, Kenken # 2, ...]

Si vous ne voulez pas garder les puzzles dans le même ordre général à chaque fois dans la boucle, je pense que votre option `` choisir au hasard, en ignorant les derniers choix '' est la meilleure. Il existe également des moyens de rendre cela un peu plus efficace; par exemple, disons que vous avez 20 choses et que vous voulez ignorer les 5 dernières cueillies. Ensuite, au lieu de choisir au hasard un numéro 1..20 et de `` relancer '' jusqu'à ce que vous en obteniez un en dehors des 5 derniers, choisissez simplement un nombre 1..15 et parcourez vos types de puzzle avec autant d'étapes, en sautant simplement tout type de puzzle qui est été sélectionné (vous pouvez le faire facilement en conservant un tableau de bits contenant les 5 derniers puzzles sélectionnés).

Steven Stadnicki
la source