Games of chance need a source of randomness. Factor's random
vocabulary provides one generic word, random, that adapts to whatever
you hand it, plus helpers for shuffling and sampling, and a way to make
randomness reproducible for tests.
inline
Some of this exercise's tasks define a word that takes a quotation (a
[ … ] block) as an argument and runs it. Factor has one firm rule
about that: a word that calls a quotation it received as an argument
must be declared inline, written by putting inline right after the
closing ;.
inline tells the compiler to splice the word's body into each call
site instead of compiling it once on its own.
Here is a small word that pushes 10 and then runs the quotation it was
handed:
USING: kernel math prettyprint ;
: with-ten ( quot -- ) 10 swap call ; inline
Its stack effect, ( quot -- ), names no outputs — yet calling it with
two quotations of different effects produces different results, all
flowing back to the caller:
[ 1 + ] with-ten . ! => 11 (quotation ( n -- n ): one value back)
[ dup 2 * ] with-ten . . ! => 20
! => 10 (quotation ( n -- n n ): two values back)
random — one elementrandom (in random) is generic over its argument:
random ( obj -- elt )
n, it returns a random integer in [0, n):USING: random ;
6 random . ! 4 (a value from 0 to 5)
{ "rock" "paper" "scissors" } random . ! "paper"
To roll a normal die you shift the range up by one: random of 6
gives 0 to 5, so 6 random 1 + gives 1 to 6.
To collect a fixed number of random draws, repeat a quotation with
replicate (in sequences):
replicate ( n quot -- seq )
USING: random sequences ;
3 [ 6 random 1 + ] replicate . ! { 4 1 6 } (three dice)
randomize — shuffle in placerandomize (in random) returns the sequence with its
elements in a random order:
randomize ( seq -- randomized )
{ 1 2 3 4 5 } randomize . ! { 3 1 5 2 4 }
It shuffles in place and returns the same sequence, so clone
first if you need to keep the original order.
sample — several distinct elementssample (in random) draws n distinct elements from a
sequence — like dealing a hand of cards, never the same card twice:
sample ( seq n -- seq' )
{ 10 20 30 40 50 } 3 sample . ! { 40 10 30 }
Asking for more elements than the sequence holds is an error.
By default random draws from a global generator seeded from the
system clock, so every run differs. For tests — or any time you want a
game you can replay — bind a generator with a fixed seed.
<mersenne-twister> (in random.mersenne-twister) builds a
generator from an integer seed, and with-random (in random)
runs a quotation with that generator installed:
<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
Everything inside the quotation — random, randomize, sample —
draws from the seeded generator, so the same seed always reproduces the
same outcome.
A word that takes a quotation as an argument and runs it through
with-random falls under the rule from Marking a word
inline: with-random ends up calling that
quotation, so such a wrapper must be marked inline.
You run the games of chance at the seaside boardwalk arcade. Punters roll dice, the claw machine grabs a prize, and the card table needs its deck shuffled and hands dealt — all powered by randomness. At the end of the night you also want a "replay" feature so a disputed game can be run again from the same seed.
Define roll-die to take the number of sides and return a roll in the
range 1 to sides (inclusive).
6 roll-die .
! => 4 (some value from 1..6)
Define pick-prize to take a sequence of prizes and return one of them
at random.
{ "teddy" "goldfish" "keyring" } pick-prize .
! => "goldfish"
Define shuffle-deck to take a deck (a sequence) and return it with the cards
in a random order.
{ 1 2 3 4 5 } shuffle-deck .
! => { 3 1 5 2 4 }
Define deal-hand to take a deck and a count n, and return n distinct
cards from the deck.
{ 10 20 30 40 50 } 3 deal-hand .
! => { 40 10 30 }
Define play-seeded to take a seed and a quotation, and run the
quotation with a generator seeded from that integer, so the same seed
always reproduces the same outcome. The quotation's result should be
returned to the caller.
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
Sign up to Exercism to learn and master Factor with 47 concepts163 exercises, and real human mentoring, all for free.