Percursos
/
Factor
Factor
/
Exercícios
/
Jogos no Passadiço
Jogos no Passadiço

Jogos no Passadiço

Exercício de aprendizagem

Introdução

Os jogos de azar precisam de uma fonte de aleatoriedade. O vocabulário random do Factor fornece uma palavra genérica, random, que se adapta ao que lhe passares, além de ajudantes para baralhar e amostrar, e uma forma de tornar a aleatoriedade reproduzível para os testes.

Marcar uma palavra como inline

Algumas tarefas deste exercício definem uma palavra que recebe uma citação (um bloco [ … ]) como argumento e a executa. O Factor tem uma regra firme sobre isso: uma palavra que faz call a uma citação que recebeu como argumento tem de ser declarada inline, o que se escreve colocando inline logo a seguir ao ; de fecho.

inline diz ao compilador para inserir o corpo da palavra em cada local de chamada, em vez de o compilar uma única vez isoladamente.

  • A citação de quem chama é inserida diretamente, pelo que tudo o que a citação deixa na pilha regressa a quem chama, mesmo que o efeito na pilha da própria palavra não declare saídas.

Eis uma palavra pequena que coloca 10 na pilha e depois executa a citação que lhe foi passada:

USING: kernel math prettyprint ;

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

O seu efeito na pilha, ( quot -- ), não nomeia nenhuma saída, mas chamá-la com duas citações de efeitos diferentes produz resultados diferentes, que regressam todos a quem chama:

[ 1 + ] with-ten .        ! => 11   (quotation ( n -- n ): one value back)
[ dup 2 * ] with-ten . .  ! => 20
                          ! => 10   (quotation ( n -- n n ): two values back)

random: um elemento

random (em random) é genérico em relação ao seu argumento:

random ( obj -- elt )
  • Dado um inteiro n, devolve um inteiro aleatório em [0, n):
USING: random ;

6 random .    ! 4   (a value from 0 to 5)
  • Dada uma sequência, devolve um elemento aleatório:
{ "rock" "paper" "scissors" } random .    ! "paper"

Para lançar um dado normal, deslocas o intervalo uma unidade para cima: random de 6 dá 0 a 5, por isso 6 random 1 + dá 1 a 6.

Sortear vários valores

Para recolher um número fixo de sorteios aleatórios, repete uma citação com replicate (em sequences):

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

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

randomize: baralhar no lugar

randomize (em random) devolve a sequência com os seus elementos numa ordem aleatória:

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

Baralha no lugar e devolve a mesma sequência, portanto faz clone primeiro se precisares de manter a ordem original.

sample: vários elementos distintos

sample (em random) retira n elementos distintos de uma sequência, como distribuir uma mão de cartas, nunca a mesma carta duas vezes:

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

Pedir mais elementos do que os que a sequência contém é um erro.

Aleatoriedade reproduzível

Por predefinição, random retira valores de um gerador global com semente do relógio do sistema, por isso cada execução é diferente. Para os testes, ou sempre que quiseres um jogo que possas reproduzir, associa um gerador com uma semente fixa.

<mersenne-twister> (em random.mersenne-twister) constrói um gerador a partir de uma semente inteira, e with-random (em random) executa uma citação com esse gerador 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

Tudo o que está dentro da citação (random, randomize, sample) retira valores do gerador com semente, por isso a mesma semente reproduz sempre o mesmo resultado.

Uma palavra que recebe uma citação como argumento e a executa através de with-random cai na regra de Marcar uma palavra como inline: with-random acaba por fazer call a essa citação, por isso uma palavra dessas tem de ser marcada como inline.

Instruções

Geres os jogos de azar na arcada do passadiço à beira-mar. Os jogadores lançam os dados, a máquina de garras apanha um prémio e a mesa de cartas precisa que se baralhe o baralho e se distribuam as mãos: tudo alimentado pelo acaso. No fim da noite, também queres uma funcionalidade de «replay», para que um jogo contestado possa ser executado outra vez a partir da mesma semente.

1. Lança um dado

Define roll-die de modo a receber o número de lados e devolver um lançamento no intervalo de 1 a sides, inclusive.

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

2. Apanha um prémio

Define pick-prize de modo a receber uma sequência de prémios e devolver um deles ao acaso.

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

3. Baralha o baralho

Define shuffle-deck de modo a receber um baralho (uma sequência) e devolvê-lo com as cartas numa ordem aleatória.

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

4. Distribui uma mão

Define deal-hand de modo a receber um baralho e um número n, e devolver n cartas distintas do baralho.

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

5. Repete a partir de uma semente

Define play-seeded de modo a receber uma semente e uma quotation, e executar a quotation com um gerador inicializado a partir desse número inteiro, para que a mesma semente reproduza sempre o mesmo resultado. O resultado da quotation deve ser devolvido a quem a chamou.

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 via GitHub A ligação abre numa nova janela ou separador
Factor Exercism

Estás pronto para começar Jogos no Passadiço?

Inscreve-te no Exercism para aprenderes e dominares Factor com 47 conceitos163 exercícios, e mentoria humana real, tudo grátis.