Parcours
/
Factor
Factor
/
Exercices
/
Jeux de la promenade
Jeux de la promenade

Jeux de la promenade

Exercice d'apprentissage

Introduction

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.

Marque un mot 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.

  • La quotation de l'appelant est insérée directement, donc tout ce que la quotation laisse sur la pile revient à l'appelant, même si l'effet de pile du mot lui-même ne nomme aucune sortie.

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ément

random (dans random) est générique vis-à-vis de son argument :

random ( obj -- elt )
  • Avec un entier n, il renvoie un entier aléatoire dans [0, n) :
USING: random ;

6 random .    ! 4   (a value from 0 to 5)
  • Avec une séquence, il renvoie un élément aléatoire :
{ "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.

Tire plusieurs valeurs

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 place

randomize (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 distincts

sample (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.

Un aléatoire reproductible

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.

Instructions

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.

1. Lance un dé

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)

2. Récupère un lot

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"

3. Mélange le paquet

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 }

4. Distribue une main

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 }

5. Rejoue à partir d'une graine

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
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Factor Exercism

Prêt à commencer Jeux de la promenade ?

Inscris-toi sur Exercism pour apprendre et maîtriser Factor avec 47 concepts163 exercices, et un vrai mentorat humain, le tout gratuitement.