Треки
/
Factor
Factor
/
Вправи
/
Ігри на набережній
Ігри на набережній

Ігри на набережній

Навчальна вправа

Вступ

Ігри на удачу потребують джерела випадковості. Словник random у Factor надає одне узагальнене слово, random, яке пристосовується до того, що ми йому передаємо, а також допоміжні засоби для перемішування й вибирання та спосіб зробити випадковість відтворюваною для тестів.

Позначення слова як inline

Деякі завдання цієї вправи визначають слово, яке приймає quotation (блок [ … ]) як аргумент і виконує його. Factor має щодо цього одне тверде правило: слово, яке застосовує call до quotation, отриманого як аргумент, потрібно оголосити як inline, що записується так: inline ставиться одразу після закривного символу ;.

inline вказує компілятору вставити тіло слова в кожне місце виклику замість того, щоб скомпілювати його окремо один раз.

  • Quotation того, хто викликає, вшивається безпосередньо, тож усе, що quotation залишає на стеку, повертається до того, хто викликав, навіть попри те, що власний стековий ефект слова не оголошує жодних вихідних даних.

Ось маленьке слово, яке кладе 10 на стек, а потім виконує переданий йому quotation:

USING: kernel math prettyprint ;

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

Його стековий ефект, ( quot -- ), не називає жодних вихідних даних, а проте виклик із двома quotation різних ефектів дає різні результати, і всі вони повертаються до того, хто викликав:

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

random: один елемент

random (у random) узагальнений щодо свого аргументу:

random ( obj -- elt )
  • Якщо дати йому ціле число n, він повертає випадкове ціле число з проміжку [0, n):
USING: random ;

6 random .    ! 4   (a value from 0 to 5)
  • Якщо дати йому послідовність, він повертає випадковий елемент:
{ "rock" "paper" "scissors" } random .    ! "paper"

Щоб кинути звичайний кубик, зсунемо діапазон на одиницю вгору: random від 6 дає від 0 до 5, тож 6 random 1 + дає від 1 до 6.

Вибирання кількох значень

Щоб зібрати фіксовану кількість випадкових значень, повторімо quotation за допомогою replicate (у sequences):

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

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

randomize: перемішування на місці

randomize (у random) повертає послідовність, елементи якої розташовані у випадковому порядку:

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

Він перемішує на місці й повертає ту саму послідовність, тож якщо потрібно зберегти початковий порядок, спершу застосуймо clone.

sample: кілька різних елементів

sample (у random) вибирає n різних елементів із послідовності, наче роздає руку карт, де та сама карта не трапляється двічі:

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

Якщо попросити більше елементів, ніж є в послідовності, це помилка.

Відтворювана випадковість

Типово random бере значення з глобального генератора, ініціалізованого від системного годинника, тож кожен запуск дає інший результат. Для тестів, або коли хочеться мати гру, яку можна перегравати, привʼяжімо генератор до фіксованого початкового значення.

<mersenne-twister> (у random.mersenne-twister) будує генератор із цілочисельного початкового значення, а with-random (у random) виконує quotation із цим генератором:

<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

Усе всередині quotation (random, randomize, sample) бере значення з ініціалізованого генератора, тож те саме початкове значення завжди відтворює той самий результат.

Слово, яке приймає quotation як аргумент і виконує його через with-random, підпадає під правило з розділу Позначення слова як inline: with-random зрештою викликає цей quotation, тож таке слово-обгортку потрібно позначити як inline.

Вказівки

Ми проводимо ігри на удачу в аркаді на морській набережній. Відвідувачі кидають кубики, автомат із захватом хапає приз, а картковий стіл потребує перетасованої колоди й розданих карт. Усе це працює на випадковості. Наприкінці вечора нам також потрібна можливість «повтору», щоб спірну гру можна було запустити знову з тим самим початковим значенням.

1. Кинути кубик

Визначте roll-die, щоб він приймав кількість сторін і повертав результат кидка в діапазоні від 1 до sides включно.

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

2. Схопити приз

Визначте pick-prize, щоб він приймав послідовність призів і повертав один із них випадково.

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

3. Перетасувати колоду

Визначте shuffle-deck, щоб він приймав колоду (послідовність) і повертав її з картами у випадковому порядку.

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

4. Роздати карти

Визначте deal-hand, щоб він приймав колоду й кількість n і повертав n різних карт з колоди.

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

5. Повторний запуск із початкового значення

Визначте play-seeded, щоб він приймав початкове значення й quotation і запускав quotation із генератором, ініціалізованим цим цілим числом, тож те саме початкове значення завжди дає той самий результат. Результат quotation потрібно повернути тому, хто її викликає.

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
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Factor Exercism

Час розпочати Ігри на набережній?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Factor, а також 47 концепцій163 вправи та справжнє наставництво від людей, і все це безкоштовно.