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

بازی‌های کارتی

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

مقدمه

یک list مجموعه‌ای تغییرپذیر از عنصرها به‌صورت متوالی است. مانند بیشتر مجموعه‌ها (به انواع درونی tuple، dict و set نگاه کنید)، لیست‌ها می‌توانند به هر نوع داده (یا چند نوع داده) ارجاع داشته باشند، از جمله به لیست‌های دیگر. مانند هر دنباله، می‌توان با شماره‌ی اندیس 0-based index از سمت چپ و -1-based index از سمت راست به عنصرها دسترسی داشت. لیست‌ها را می‌توان به‌طور کامل یا جزئی با نماد برش یا <list>.copy() کپی کرد.

لیست‌ها از عملیات‌های دنباله‌ای هم عمومی و هم تغییرپذیر پشتیبانی می‌کنند، مانند min()/max()، <list>.index()، <list>.append() و <list>.reverse(). می‌توان با ساختار for item in <list> روی عنصرهای لیست پیمایش کرد. وقتی هم اندیس و هم مقدار عنصر لازم باشد، می‌توان از for index, item in enumerate(<list>) استفاده کرد.

در پشت صحنه، lists به‌صورت آرایه‌های پویا پیاده‌سازی می‌شوند، مشابه نوع ArrayList در Java، و بیشتر برای نگهداری گروه‌هایی از داده‌های مشابه (رشته‌ها، اعداد، مجموعه‌ها و غیره) با طول نامعلوم به کار می‌روند. لیست‌ها ساختار داده‌ای بسیار انعطاف‌پذیر و پرکاربردی هستند و بسیاری از متدها و عملیات درونی در Python، لیست را به‌عنوان خروجی تولید می‌کنند.

ساخت

یک list را می‌توان به‌صورت تحت‌اللفظی با کروشه‌های مربعی [] و ویرگول بین عنصرها تعریف کرد:

>>> no_elements = []

>>> no_elements
[]

>>> one_element = ["Guava"]

>>> one_element
['Guava']

>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]

>>> elements_separated_with_commas
['Parrot', 'Bird', 334782]

برای خوانایی، وقتی عنصرهای زیادی وجود داشته باشد یا ساختارهای داده‌ای تودرتو درون یک list قرار داشته باشند، می‌توان از شکست خط استفاده کرد:

>>> lots_of_entries = [
      "Rose",
      "Sunflower",
      "Poppy",
      "Pansy",
      "Tulip",
      "Fuchsia",
      "Cyclamen",
      "Lavender"
   ]
   
>>> lots_of_entries
['Rose', 'Sunflower', 'Poppy', 'Pansy', 'Tulip', 'Fuchsia', 'Cyclamen', 'Lavender']

# Each data structure is on its own line to help clarify what they are.
>>> nested_data_structures = [
      {"fish": "gold", "monkey": "brown", "parrot": "grey"},
      ("fish", "mammal", "bird"),
      ['water', 'jungle', 'sky']
   ]
   
>>> nested_data_structures
[{'fish': 'gold', 'monkey': 'brown', 'parrot': 'grey'}, ('fish', 'mammal', 'bird'), ['water', 'jungle', 'sky']]

سازنده‌ی list() را می‌توان خالی یا با یک شیء قابل پیمایش به‌عنوان آرگومان به کار برد. سازنده عنصرهای شیء قابل پیمایش را یکی‌یکی می‌پیماید و به ترتیب به list اضافه می‌کند:

>>> no_elements = list()

>>> no_elements
[]

# The tuple is unpacked and each element is added.
>>> multiple_elements_from_tuple = list(("Parrot", "Bird", 334782))

>>> multiple_elements_from_tuple
['Parrot', 'Bird', 334782]

# The set is unpacked and each element is added.
>>> multiple_elements_from_set = list({2, 3, 5, 7, 11})

>>> multiple_elements_from_set
[2, 3, 5, 7, 11]

نتیجه‌ی استفاده از سازنده‌ی list با یک string یا یک dict ممکن است غافلگیرکننده باشد:

# String elements (Unicode code points) are iterated through and added *individually*.
>>> multiple_elements_string = list("Timbuktu")

>>> multiple_elements_string
['T', 'i', 'm', 'b', 'u', 'k', 't', 'u']

# Unicode separators and positioning code points are also added *individually*.
>>> multiple_code_points_string = list('अभ्यास')

>>> multiple_code_points_string
['अ', 'भ', '्', 'य', 'ा', 'स']

# The iteration default for dictionaries is over the keys, so only key data is inserted into the list.
>>> source_data = {"fish": "gold", "monkey": "brown"}

>>> multiple_elements_dict_1 = list(source_data)
['fish', 'monkey']

چون سازنده‌ی list فقط اشیاء قابل پیمایش (یا هیچ آرگومانی) را به‌عنوان آرگومان می‌پذیرد، اشیائی که قابل پیمایش نیستند خطای نوع می‌اندازند. در نتیجه، ساختن یک list تک‌عنصری با روش تحت‌اللفظی بسیار آسان‌تر است.

# Numbers are not iterable, and so attempting to create a list with a number passed to the constructor fails.
>>> one_element = list(16)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

# Tuples *are* iterable, so passing a one-element tuple to the constructor does work, but it's awkward
>>> one_element_from_iterable = list((16,))

>>> one_element_from_iterable
[16]

دسترسی به عنصرها

به عنصرهای درون لیست‌ها (و همچنین عنصرها در دیگر انواع دنباله‌ای str و tuple) می‌توان با 0-based index و نمادگذاری کروشه‌ای دسترسی داشت. اندیس‌ها می‌توانند از left --> right (با شروع از صفر) یا از right --> left (با شروع از -۱) باشند.

اندیس از چپ ⟹






۰
👇🏾
۱
👇🏾
۲
👇🏾
۳
👇🏾
۴
👇🏾
۵
👇🏾
P y t h o n
👆🏾
-۶
👆🏾
-۵
👆🏾
-۴
👆🏾
-۳
👆🏾
-۲
👆🏾
-۱





⟸ اندیس از راست
>>> breakfast_foods = ["Oatmeal", "Fruit Salad", "Eggs", "Toast"]

# Oatmeal is at index 0 or index -4.
>>> breakfast_foods[0]
'Oatmeal'

>>> breakfast_foods[-4]
'Oatmeal'

# Eggs are at index -2 or 2
>>> breakfast_foods[-2]
'Eggs'

>>> breakfast_foods[2]
'Eggs'

# Toast is at -1
>>> breakfast_foods[-1]
'Toast'

می‌توان به بخشی از عنصرهای درون یک list با نماد برش (<list>[start:stop]) دسترسی داشت. یک برش دنباله‌ای از عنصرها در موقعیت index است، به‌گونه‌ای که start <= index < stop. برش‌زدن نسخه‌ای از عنصرهای «برش‌خورده» را برمی‌گرداند و list اصلی را تغییر نمی‌دهد.

می‌توان از پارامتر step هم به‌صورت [start:stop:step] استفاده کرد تا از عنصرهای list «رد شد» یا آن‌ها را فیلتر کرد (برای مثال، step برابر ۲ عنصرها را یکی در میان در بازه انتخاب می‌کند):

>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]

# If there is no step parameter, the step is assumed to be 1.
>>> middle_colors = colors[2:6]

>>> middle_colors
['Green', 'Yellow', 'Orange', 'Pink']

# If the start or stop parameters are omitted, the slice will
# start at index zero, and will stop at the end of the list.
>>> primary_colors = colors[::3]

>>> primary_colors
['Red', 'Yellow', 'Blue']

کار با لیست‌ها

استفاده از تابع درونی sum() روی یک لیست، مجموع همه‌ی اعداد درون لیست را برمی‌گرداند:

>>> number_list = [1, 2, 3, 4]
>>> sum(number_list)
10

می‌توانید طول یک لیست را هم با تابع len() به دست آورید:

>>> long_list = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
>>> len(long_list)
10

لیست‌ها را می‌توان به روش‌های متفاوتی نیز ترکیب کرد:

# Using the plus + operator unpacks each list and creates a new list, but it is not efficient.
>>> new_via_concatenate = ["George", 5] + ["cat", "Tabby"]

>>> new_via_concatenate
['George', 5, 'cat', 'Tabby']

# Likewise, using the multiplication operator * is the equivalent of using + n times.
>>> first_group = ["cat", "dog", "elephant"]
>>> multiplied_group = first_group * 3

>>> multiplied_group
['cat', 'dog', 'elephant', 'cat', 'dog', 'elephant', 'cat', 'dog', 'elephant']

لیست‌ها یک پیمایشگر فراهم می‌کنند و می‌توان مانند دیگر انواع دنباله‌ای روی آن‌ها حلقه زد.

#  Looping through the list and printing out each element.
>>> colors = ["Orange", "Green", "Grey", "Blue"]

>>> for item in colors:
...     print(item)
...
Orange
Green
Grey
Blue

برای توضیحی مفصل‌تر درباره‌ی loops و iterators، مفهوم loops را کامل کنید.

دستورالعمل‌ها

Elyse واقعاً بی‌صبرانه منتظر است که در سفر پیش‌رویش به Vegas کمی پوکر (و بازی‌های کارتی دیگر) بازی کند. او که طرفدار پر و پا قرص «خودردیابی» است، می‌خواهد چند تابع کوچک بسازد که در کارهای پیگیری به او کمک کند و از شما خواسته است که در فکر کردن به آن‌ها یاری‌اش کنید.

1. پیگیری دورهای پوکر

Elyse به‌ویژه پوکر را دوست دارد و می‌خواهد پیگیری کند که چند دور بازی می‌کند و آن‌ها کدام دورها هستند. هر دور شماره‌ی خودش را دارد و هر میز، شماره‌ی دوری را که در حال بازی است نشان می‌دهد. Elyse یک میز انتخاب می‌کند و می‌نشیند تا اولین دورش را بازی کند. قصد دارد سه دور بازی کند.

تابعی به اسم get_rounds(<round_number>) پیاده‌سازی کنید که شماره‌ی دور فعلی را می‌گیرد و یک list برمی‌گرداند که شامل آن دور و دو دور بعدی است که پیش می‌آیند:

>>> get_rounds(27)
[27, 28, 29]

2. نگه‌داشتن همه‌ی دورها در یک جا

Elyse چند دور در میز اول بازی کرد، بعد استراحت کرد و چند دور دیگر در میز دوم بازی کرد... اما در پایان برای هر میز یک لیست متفاوت داشت! او می‌خواهد آن دو لیست را کنار هم بگذارد تا بتواند همه‌ی دورهای پوکر را در یک جا پیگیری کند.

تابعی به اسم concatenate_rounds(<rounds_1>, <rounds_2>) پیاده‌سازی کنید که دو لیست می‌گیرد و یک list برمی‌گرداند که از همه‌ی دورهای موجود در list اول و سپس همه‌ی دورهای موجود در list دوم تشکیل شده است:

>>> concatenate_rounds([27, 28, 29], [35, 36])
[27, 28, 29, 35, 36]

3. یافتن دورهای قبلی

وقتی صحبت از برخی از دورهای قبلی پوکر می‌شود، بازیکن دیگری اشاره می‌کند که دو تای آن‌ها چقدر شبیه هم پیش رفتند. Elyse مطمئن نیست که آن دورها را بازی کرده است یا نه.

تابعی به اسم list_contains_round(<rounds>, <round_number>) پیاده‌سازی کنید که دو ورودی می‌گیرد: یک لیست از دورهای بازی‌شده و یک شماره‌ی دور. این تابع اگر آن دور در لیست دورهای بازی‌شده باشد True و در غیر این صورت False برمی‌گرداند:

>>> list_contains_round([27, 28, 29, 35, 36], 29)
True

>>> list_contains_round([27, 28, 29, 35, 36], 30)
False

4. میانگین‌گیری از مقادیر کارت‌ها

Elyse می‌خواهد بازی جدیدی به اسم Black Joe را امتحان کند. این بازی شبیه Black Jack است، یعنی هدف این است که مجموع کارت‌های دستتان به یک مقدار مشخص برسد، اما در Black Joe هدف این است که میانگین مقادیر کارت‌ها ۷ شود. میانگین را می‌توان با جمع کردن همه‌ی مقادیر کارت‌ها و سپس تقسیم آن مجموع بر تعداد کارت‌های دست به دست آورد.

تابعی به اسم card_average(<hand>) پیاده‌سازی کنید که میانگین مقادیر یک دست Black Joe را برمی‌گرداند.

>>> card_average([5, 6, 7])
6.0

5. میانگین‌های جایگزین

در Black Joe، سرعت مهم است. Elyse می‌خواهد روشی سریع‌تر برای پیدا کردن میانگین پیدا کند.

او به دو روش برای رسیدن به عددی شبیه میانگین فکر کرده است:

  • میانگین اولین و آخرین عدد دست را بگیرید.
  • از میانه (کارت وسط) دست استفاده کنید.

تابع approx_average_is_average(<hand>) را پیاده‌سازی کنید؛ در اینجا hand لیستی است که مقادیر کارت‌های دستتان را در خود دارد.

اگر یکی or هر دو از روش‌های نام‌برده‌شده در بالا به عددی برابر با میانگین واقعی برسند، True برگردانید.

نکته: طول همه‌ی دست‌ها فرد است تا پیدا کردن میانه ساده‌تر شود.

>>> approx_average_is_average([1, 2, 3])
True

>>> approx_average_is_average([2, 3, 4, 8, 8])
True

>>> approx_average_is_average([1, 2, 3, 5, 9])
False

6. تکنیک‌های بیشتر برای میانگین‌گیری

Elyse که از نتایج آزمایش میانگین‌گیری‌اش کنجکاو شده، می‌خواهد بداند آیا گرفتن میانگین کارت‌های مکان‌های زوج در مقابل میانگین کارت‌های مکان‌های فرد نتیجه‌ی یکسانی می‌دهد یا نه. وقت یک تابع آزمایشی دیگر است!

تابعی به اسم average_even_is_average_odd(<hand>) پیاده‌سازی کنید که یک «مقدار منطقی» برمی‌گرداند و نشان می‌دهد آیا میانگین کارت‌های ایندکس‌های زوج با میانگین کارت‌های ایندکس‌های فرد یکسان است یا نه.

>>> average_even_is_average_odd([1, 2, 3])
True

>>> average_even_is_average_odd([1, 2, 3, 4])
False

7. قواعد دور امتیازی

هر یازدهمین دست در Black Joe یک دست امتیازی با یک قاعده‌ی امتیازی است: اگر آخرین کارتی که می‌کشید یک جک باشد، ارزش آن را دو برابر می‌کنید.

تابعی به اسم maybe_double_last(<hand>) پیاده‌سازی کنید که یک دست می‌گیرد و بررسی می‌کند آیا آخرین کارت یک جک (۱۱) است. اگر آخرین کارت واقعاً جک (۱۱) باشد، پیش از برگرداندن دست، ارزش آن را دو برابر کنید.

>>> hand = [5, 9, 11]
>>> maybe_double_last(hand)
[5, 9, 22]

>>> hand = [5, 9, 10]
>>> maybe_double_last(hand)
[5, 9, 10]
ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
Python Exercism

آماده‌اید بازی‌های کارتی را شروع کنید؟

در Exercism ثبت‌نام کنید تا Python را همراه با 17 مفهوم146 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.