Tracks
/
Factor
Factor
/
Ejercicios
/
Juegos del paseo marítimo
Juegos del paseo marítimo

Juegos del paseo marítimo

Ejercicio de aprendizaje

Introducción

Los juegos de azar necesitan una fuente de aleatoriedad. El vocabulario random de Factor te ofrece una palabra genérica, random, que se adapta a lo que le entregues, además de ayudantes para barajar y tomar muestras, y una forma de hacer que la aleatoriedad sea reproducible para las pruebas.

Marcar una palabra como inline

Algunas de las tareas de este ejercicio definen una palabra que recibe una quotation (un bloque [ … ]) como argumento y la ejecuta. Factor tiene una regla firme al respecto: una palabra que hace call a una quotation que recibió como argumento debe declararse inline, y se escribe poniendo inline justo después del ; de cierre.

inline le indica al compilador que inserte el cuerpo de la palabra en cada lugar donde se la llama, en vez de compilarla una sola vez por su cuenta.

  • La quotation de quien llama se inserta directamente, así que lo que la quotation deje en la pila vuelve a quien la llamó, aunque el propio efecto de pila de la palabra no declare ninguna salida.

Aquí tienes una palabra pequeña que coloca 10 en la pila y luego ejecuta la quotation que recibió:

USING: kernel math prettyprint ;

: with-ten ( quot -- ) 10 swap call ; inline

Su efecto de pila, ( quot -- ), no nombra ninguna salida, y sin embargo, si la llamas con dos quotations de efectos distintos, obtienes resultados distintos, todos de vuelta hacia quien llamó:

[ 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 elemento

random (en random) es genérico respecto de su argumento:

random ( obj -- elt )
  • Si le das un entero n, devuelve un entero aleatorio en [0, n):
USING: random ;

6 random .    ! 4   (a value from 0 to 5)
  • Si le das una secuencia, devuelve un elemento aleatorio:
{ "rock" "paper" "scissors" } random .    ! "paper"

Para tirar un dado normal, desplazas el rango en uno: random de 6 da de 0 a 5, así que 6 random 1 + da de 1 a 6.

Sacar varios valores

Para reunir una cantidad fija de extracciones aleatorias, repite una quotation con replicate (en sequences):

replicate ( n quot -- seq )
USING: random sequences ;

3 [ 6 random 1 + ] replicate .    ! { 4 1 6 }   (three dice)

randomize: barajar in situ

randomize (en random) devuelve la secuencia con sus elementos en un orden aleatorio:

randomize ( seq -- randomized )
{ 1 2 3 4 5 } randomize .    ! { 3 1 5 2 4 }

Baraja in situ y devuelve la misma secuencia, así que haz clone primero si necesitas conservar el orden original.

sample: varios elementos distintos

sample (en random) saca n elementos distintos de una secuencia, como repartir una mano de cartas: nunca la misma carta dos veces:

sample ( seq n -- seq' )
{ 10 20 30 40 50 } 3 sample .    ! { 40 10 30 }

Pedir más elementos de los que contiene la secuencia es un error.

Aleatoriedad reproducible

De forma predeterminada, random toma valores de un generador global inicializado con el reloj del sistema, así que cada ejecución es distinta. Para las pruebas, o cada vez que quieras un juego que puedas volver a jugar, asocia un generador con una semilla fija.

<mersenne-twister> (en random.mersenne-twister) construye un generador a partir de una semilla entera, y with-random (en random) ejecuta una quotation con ese generador instalado:

<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

Todo lo que está dentro de la quotation (random, randomize, sample) toma valores del generador con semilla, así que la misma semilla siempre reproduce el mismo resultado.

Una palabra que recibe una quotation como argumento y la ejecuta a través de with-random entra en la regla de Marcar una palabra como inline: with-random termina haciendo call a esa quotation, así que ese tipo de envoltorio debe marcarse inline.

Instrucciones

Diriges los juegos de azar en la sala de juegos del paseo marítimo. Los jugadores tiran los dados, la máquina de garra atrapa un premio y la mesa de cartas necesita que se baraje el mazo y se repartan las manos. Todo funciona gracias al azar. Al final de la noche también quieres una funcionalidad de «repetición» para poder volver a ejecutar una partida en disputa a partir de la misma semilla.

1. Tira un dado

Define roll-die para que reciba el número de caras y devuelva una tirada en el rango de 1 a sides (inclusive).

6 roll-die .
! => 4   (some value from 1..6)

2. Atrapa un premio

Define pick-prize para que reciba una secuencia de premios y devuelva uno de ellos al azar.

{ "teddy" "goldfish" "keyring" } pick-prize .
! => "goldfish"

3. Baraja el mazo

Define shuffle-deck para que reciba un mazo (una secuencia) y lo devuelva con las cartas en un orden aleatorio.

{ 1 2 3 4 5 } shuffle-deck .
! => { 3 1 5 2 4 }

4. Reparte una mano

Define deal-hand para que reciba un mazo y una cantidad n, y devuelva n cartas distintas del mazo.

{ 10 20 30 40 50 } 3 deal-hand .
! => { 40 10 30 }

5. Repite la partida desde una semilla

Define play-seeded para que reciba una semilla y una quotation, y ejecute la quotation con un generador inicializado con esa semilla entera, de modo que la misma semilla siempre reproduzca el mismo resultado. El resultado de la quotation debe devolverse a quien la llama.

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
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
Factor Exercism

¿Todo listo para empezar Juegos del paseo marítimo?

Regístrate en Exercism para aprender y dominar Factor con 47 conceptos163 ejercicios y mentoría humana real, todo gratis.