কার্ড গেম

কার্ড গেম

লার্নিং অনুশীলনী

ভূমিকা

একটি 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 ಗುলো ডাইনামিক অ্যারে হিসেবে ইমপ্লিমেন্ট করা হয়, অনেকটা Java-এর ArrayList টাইপের মতো, এবং সাধারণত অজানা দৈর্ঘ্যের একই ধরনের ডেটার (স্ট্রিং, সংখ্যা, সেট ইত্যাদি) দল জমা রাখতে ব্যবহার করা হয়। অ্যারে একটি অত্যন্ত নমনীয় ও দরকারি ডেটা স্ট্রাকচার, আর 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]

একটি string বা dict দিয়ে list কনস্ট্রাক্টর ব্যবহার করলে ফলাফল চমকে দেওয়ার মতো হতে পারে:

# 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 (-1 থেকে শুরু) হতে পারে।

বাম দিক থেকে ইনডেক্স ⟹






0
👇🏾
1
👇🏾
2
👇🏾
3
👇🏾
4
👇🏾
5
👇🏾
P y t h o n
👆🏾
-6
👆🏾
-5
👆🏾
-4
👆🏾
-3
👆🏾
-2
👆🏾
-1





⟸ ডান দিক থেকে ইনডেক্স
>>> 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 পরিবর্তন করে না।

list-এর এলিমেন্টগুলো "স্কিপ" বা ফিল্টার করতে [start:stop:step]-এ একটি step প্যারামিটারও ব্যবহার করা যায় (যেমন, step 2 হলে রেঞ্জের প্রতি দ্বিতীয় এলিমেন্ট বাছাই করা হবে):

>>> 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 কনসেপ্টটি সম্পূর্ণ করুন।

নির্দেশনা

এলিস তার আসন্ন ভেগাস ভ্রমণে কিছু পোকার (এবং অন্যান্য কার্ড গেম) খেলার জন্য দারুণ উৎসাহী। "সেলফ-ট্র্যাকিং"-এর একজন বড় ভক্ত হিসেবে সে কিছু ছোট ফাংশন বানাতে চায়, যা ট্র্যাকিংয়ের কাজে তাকে সাহায্য করবে, আর সেগুলো ভেবে দেখতে সে আপনার সাহায্য চেয়েছে।

1. পোকার রাউন্ড ট্র্যাক করা

এলিস বিশেষভাবে পোকার পছন্দ করে, আর সে কতগুলো রাউন্ড খেলে সেটি ট্র্যাক করতে চায়, এবং সেগুলো কোন কোন রাউন্ড। প্রতিটি রাউন্ডের নিজস্ব নম্বর থাকে, আর প্রতিটি টেবিলে বর্তমানে খেলা হচ্ছে এমন রাউন্ড নম্বর দেখানো হয়। এলিস একটি টেবিল বেছে নিয়ে তার প্রথম রাউন্ড খেলতে বসে। সে তিনটি রাউন্ড খেলার পরিকল্পনা করেছে।

এমন একটি ফাংশন get_rounds(<round_number>) ইমপ্লিমেন্ট করুন যা বর্তমান রাউন্ড নম্বরটি নেয় এবং সেই রাউন্ড ও পরের দুটি আসন্ন রাউন্ডসহ একটি list রিটার্ন করে:

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

2. সব রাউন্ড একই জায়গায় রাখা

এলিস প্রথম টেবিলে কয়েকটি রাউন্ড খেলল, তারপর একটু বিরতি নিয়ে দ্বিতীয় টেবিলে আরও কিছু রাউন্ড খেলল ... কিন্তু শেষে প্রতিটি টেবিলের জন্য আলাদা আলাদা অ্যারে হয়ে গেল! সে দুটি অ্যারে একসাথে জোড়া লাগাতে চায়, যাতে সব পোকার রাউন্ড একই জায়গায় ট্র্যাক করতে পারে।

এমন একটি ফাংশন concatenate_rounds(<rounds_1>, <rounds_2>) ইমপ্লিমেন্ট করুন যা দুটি অ্যারে নেয় এবং প্রথম list-এর সব রাউন্ড, তারপর দ্বিতীয় list-এর সব রাউন্ড নিয়ে গঠিত একটি list রিটার্ন করে:

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

3. আগের রাউন্ড খুঁজে বের করা

আগের কিছু পোকার রাউন্ডের কথা প্রসঙ্গে আরেকজন খেলোয়াড় মন্তব্য করে যে এর মধ্যে দুটি রাউন্ড বেশ একইভাবে হয়েছিল। এলিস নিশ্চিত নয় সে ওই রাউন্ডগুলো খেলেছিল কি না।

এমন একটি ফাংশন 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. কার্ডের মানের গড় বের করা

এলিস Black Joe নামের একটি নতুন গেম খেলতে চায়। এটি Black Jack-এর মতো, যেখানে আপনার লক্ষ্য হলো হাতের কার্ডগুলোর যোগফল একটি টার্গেট ভ্যালু হওয়া, কিন্তু Black Joe-তে লক্ষ্য হলো কার্ডের মানগুলোর গড় ৭ হওয়া। সব কার্ডের মান যোগ করে সেই যোগফলকে হাতের কার্ডের সংখ্যা দিয়ে ভাগ করলে গড় পাওয়া যায়।

এমন একটি ফাংশন card_average(<hand>) ইমপ্লিমেন্ট করুন যা Black Joe-এর একটি হাতের গড় মান রিটার্ন করবে।

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

5. বিকল্প গড়

Black Joe-তে গতি গুরুত্বপূর্ণ। এলিস গড় বের করার একটি দ্রুততর উপায় খুঁজে দেখতে চায়।

সে গড়ের মতো একটি সংখ্যা বের করার দুটি উপায় ভেবেছে:

  • হাতের প্রথম ও শেষ সংখ্যার গড় নেওয়া।
  • হাতের মিডিয়ান (মাঝের কার্ড) ব্যবহার করা।

এমন একটি ফাংশন 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. আরও গড় বের করার কৌশল

নিজের গড় পরীক্ষার ফলাফলে আগ্রহী হয়ে এলিস ভাবছে, জোড় অবস্থানের কার্ডগুলোর গড় আর বিজোড় অবস্থানের কার্ডগুলোর গড় একই ফল দেবে কি না। আরেকটি টেস্ট ফাংশনের সময় এসেছে!

এমন একটি ফাংশন 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টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।