Les jeux de hasard ont besoin d'une source d'aléatoire. Le vocabulaire random de Factor en fournit une : un mot générique, random, qui s'adapte à ce que tu lui passes, ainsi que des outils pour mélanger et échantillonner des éléments, et un moyen de rendre l'aléatoire reproductible pour les tests.
inline
Certaines tâches de cet exercice définissent un mot qui prend en argument une quotation (un bloc [ … ]) et l'exécute. Factor a une règle stricte à ce sujet : un mot qui call une quotation reçue en argument doit être déclaré inline, ce qui s'écrit en plaçant inline juste après le ; final.
inline indique au compilateur d'insérer le corps du mot à chaque site d'appel plutôt que de le compiler une fois pour toutes.
Voici un petit mot qui empile 10 puis exécute la quotation qu'on lui a passée :
USING: kernel math prettyprint ;
: with-ten ( quot -- ) 10 swap call ; inline
Son effet de pile, ( quot -- ), ne nomme aucune sortie, et pourtant l'appeler avec deux quotations d'effets différents produit des résultats différents, qui reviennent tous à l'appelant :
[ 1 + ] with-ten . ! => 11 (quotation ( n -- n ): one value back)
[ dup 2 * ] with-ten . . ! => 20
! => 10 (quotation ( n -- n n ): two values back)
random : un élémentrandom (dans random) est générique vis-à-vis de son argument :
random ( obj -- elt )
n, il renvoie un entier aléatoire dans [0, n) :USING: random ;
6 random . ! 4 (a value from 0 to 5)
{ "rock" "paper" "scissors" } random . ! "paper"
Pour lancer un dé classique, il faut décaler l'intervalle de un : random avec 6 donne de 0 à 5, donc 6 random 1 + donne de 1 à 6.
Pour recueillir un nombre fixe de tirages aléatoires, répète une quotation avec replicate (dans sequences) :
replicate ( n quot -- seq )
USING: random sequences ;
3 [ 6 random 1 + ] replicate . ! { 4 1 6 } (three dice)
randomize : mélange en placerandomize (dans random) renvoie la séquence avec ses éléments dans un ordre aléatoire :
randomize ( seq -- randomized )
{ 1 2 3 4 5 } randomize . ! { 3 1 5 2 4 }
Il mélange en place et renvoie la même séquence, donc fais un clone d'abord si tu veux conserver l'ordre d'origine.
sample : plusieurs éléments distinctssample (dans random) tire n éléments distincts d'une séquence, comme on distribue une main de cartes, jamais deux fois la même carte :
sample ( seq n -- seq' )
{ 10 20 30 40 50 } 3 sample . ! { 40 10 30 }
Demander plus d'éléments que la séquence n'en contient provoque une erreur.
Par défaut, random puise dans un générateur global initialisé à partir de l'horloge système, donc chaque exécution diffère. Pour les tests, ou chaque fois que tu veux un jeu que tu peux rejouer, associe un générateur à une graine fixe.
<mersenne-twister> (dans random.mersenne-twister) construit un générateur à partir d'une graine entière, et with-random (dans random) exécute une quotation avec ce générateur installé :
<mersenne-twister> ( seed -- rnd )
with-random ( rnd quot -- )
USING: random random.mersenne-twister ;
42 <mersenne-twister> [ 6 random 1 + ] with-random .
! always the same value for seed 42
Tout ce qui se trouve dans la quotation (random, randomize, sample) puise dans le générateur initialisé, donc la même graine reproduit toujours le même résultat.
Un mot qui prend une quotation en argument et l'exécute via with-random relève de la règle de la section Marque un mot inline : with-random finit par faire un call sur cette quotation, donc un tel mot enveloppe doit être marqué inline.
Tu tiens les jeux de hasard de la salle d'arcade du bord de mer. Les joueurs lancent les dés, la machine à pince attrape un lot, et la table de cartes a besoin qu'on mélange son paquet et qu'on distribue les mains, le tout grâce au hasard. À la fin de la soirée, tu veux aussi une fonctionnalité de « rejeu » pour pouvoir rejouer une partie contestée à partir de la même graine.
Définis roll-die pour qu'il prenne le nombre de faces et renvoie un lancer compris entre 1 et sides, bornes incluses.
6 roll-die .
! => 4 (some value from 1..6)
Définis pick-prize pour qu'il prenne une séquence de lots et en renvoie un au hasard.
{ "teddy" "goldfish" "keyring" } pick-prize .
! => "goldfish"
Définis shuffle-deck pour qu'il prenne un paquet (une séquence) et le renvoie avec les cartes dans un ordre aléatoire.
{ 1 2 3 4 5 } shuffle-deck .
! => { 3 1 5 2 4 }
Définis deal-hand pour qu'il prenne un paquet et un nombre n, et renvoie n cartes distinctes du paquet.
{ 10 20 30 40 50 } 3 deal-hand .
! => { 40 10 30 }
Définis play-seeded pour qu'il prenne une graine et une quotation, et exécute la quotation avec un générateur initialisé à partir de cet entier, afin que la même graine reproduise toujours le même résultat. Le résultat de la quotation doit être renvoyé à l'appelant.
42 [ 6 roll-die ] play-seeded .
! => the same value every time for seed 42
! Same seed, same game:
99 [ 5 deal-hand ] play-seeded 99 [ 5 deal-hand ] play-seeded = .
! => t
Inscris-toi sur Exercism pour apprendre et maîtriser Factor avec 47 concepts163 exercices, et un vrai mentorat humain, le tout gratuitement.