مسیرها
/
Factor
Factor
/
تمرین‌ها
/
بازی‌های اسکله
بازی‌های اسکله

بازی‌های اسکله

تمرین یادگیری

مقدمه

بازی‌های شانسی به منبعی از تصادف نیاز دارند. واژگان random در Factor یک «واژه»ی جنریک به اسم random در اختیارتان می‌گذارد که با هرچه به آن بدهید سازگار می‌شود، به‌همراه ابزارهایی برای درهم‌ریختن و نمونه‌گیری، و راهی برای تکرارپذیر کردن تصادف برای تست‌ها.

علامت‌گذاری یک واژه با inline

برخی از کارهای این تمرین واژه‌ای را تعریف می‌کنند که یک «کوتیشن» (یک بلوک [ … ]) را به عنوان آرگومان می‌گیرد و آن را اجرا می‌کند. Factor یک قانون محکم در این مورد دارد: واژه‌ای که کوتیشنی را که به عنوان آرگومان گرفته است با call فراخوانی می‌کند باید inline اعلام شود، که با گذاشتن inline بلافاصله بعد از ; پایانی نوشته می‌شود.

inline به کامپایلر می‌گوید که بدنه‌ی واژه را به‌جای کامپایل یک‌باره و مستقل آن، در هر محل فراخوانی جای‌گذاری کند.

  • کوتیشن فراخوانی‌کننده مستقیماً بخیه می‌خورد، بنابراین هرچه کوتیشن روی پشته باقی می‌گذارد به فراخوانی‌کننده برمی‌گردد، حتی اگر اثر پشته‌ی خودِ واژه هیچ خروجی‌ای اعلام نکند.

این یک واژه‌ی کوچک است که 10 را روی پشته می‌گذارد و بعد کوتیشنی را که گرفته است اجرا می‌کند:

USING: kernel math prettyprint ;

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

اثر پشته‌ی آن، ( quot -- )، هیچ خروجی‌ای را نام نمی‌برد، اما فراخوانی آن با دو کوتیشن با اثرهای متفاوت نتایج متفاوتی تولید می‌کند که همه‌شان به فراخوانی‌کننده برمی‌گردند:

[ 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 عدد ۰ تا ۵ را می‌دهد، پس 6 random 1 + عدد ۱ تا ۶ را می‌دهد.

گرفتن چند مقدار

برای جمع کردن تعداد ثابتی قرعه‌کشی تصادفی، یک کوتیشن را با 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) یک کوتیشن را با آن مولد نصب‌شده اجرا می‌کند:

<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

هر چیزی داخل کوتیشن (random، randomize، sample) از مولد دانه‌دار بیرون می‌کشد، بنابراین دانه‌ی یکسان همیشه نتیجه‌ی یکسان تولید می‌کند.

واژه‌ای که یک کوتیشن را به عنوان آرگومان می‌گیرد و آن را از with-random می‌گذراند، زیر قانونِ علامت‌گذاری یک واژه با inline قرار می‌گیرد: with-random در نهایت آن کوتیشن را call می‌کند، پس چنین پوششی باید 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 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.