شطرنج میوه‌ای



می‌خواهیم تابعی بنویسیم که کارش چک کردن شرایط تعریف شده برای اشیاء (objects) مورد نظر است.


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

میوه‌‌‌ای خوب است که شرایط زیر را داشته باشد:

  • شکل آن به صورت کروی (sphere) باشد.
  • جرم آن بین ۳۰۰ تا ۶۰۰ گرم باشد.
  • حجم آن بین ۱۰۰ تا ۵۰۰ سانتی‌متر مکعب باشد.

توجه کنید که جرم میوه‌های خوب، خود اعداد ۳۰۰ و ۶۰۰ و حجم آن، خود اعداد ۱۰۰ و ۵۰۰ ‌هم می‌تواند باشد.

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

جزئیات🔗

  • تابع شما که باید به نام fruits باشد به عنوان ورودی لیستی(tuple) از لغت‌نامه‌‌‌ها‌(Dictionary) دریافت می‌کند که هر لغت‌نامه اطلاعات مربوط به یک میوه می‌باشد. این تابع در نهایت باید تعداد هر کدام از میوه‌های خوب را در قالب یک لغت‌نامه برگرداند.
  • نام و شکل هر میوه به ترتیب با عنوان name و shape و جرم و حجم هر میوه به ترتیب با عنوان mass و volume در یک لغت‌نامه ذخیره شده‌ است.
  • فرض کنید ۴ عدد میوه به صورت زیر داریم:
نام میوه شکل جرم حجم وضیعت
سیب کروی ۳۵۰ ۱۲۰ میوه خوب
انبه مربعی ۱۵۰ ۱۲۰ میوه بد
لیمو کروی ۳۰۰ ۱۰۰ میوه خوب
سیب کروی ۵۰۰ ۲۵۰ میوه خوب

سیستم‌ داوری برای ورودی دادن میوه‌های بالا به تابع fruits این تابع را به صورت زیر صدا می‌زند.

output = fruits ((
    {'name':'apple', 'shape': 'sphere', 'mass': 350, 'volume': 120},
    {'name':'mango', 'shape': 'square', 'mass': 150, 'volume': 120}, 
    {'name':'lemon', 'shape': 'sphere', 'mass': 300, 'volume': 100},
    {'name':'apple', 'shape': 'sphere', 'mass': 500, 'volume': 250}))
Python

در بین این میوه‌ها، سه تا میوه خوب وجود دارد که ۲ تای آن‌ها سیب، و یکی از آن‌ها لیمو است. خروجی تابع شما به ازای چنین چیزی باید یک لغت‌نامه به صورت زیر باشد:

{'apple': 2, 'lemon': 1}
Python

ساختار تابع شما باید به شکل زیر باشد:

def fruits(tuple_of_fruits):
   pass
Python

نکات🔗

  • برای ارسال باید یک فایل پایتون ارسال کنید که در آن تابع fruits به شکل گفته شده وجود داشته باشد.
  • می‌توانید فایل اولیه‌ی خام و کد تست‌ نمونه را با استفاده از این لینک دانلود کنید.

قسمت آموزشی🔗

در این قسمت راهنمایی‌های سوال به ترتیب در روزهای شنبه، دوشنبه و چهارشنبه ساعت ۱۸ اضافه می‌شود. مشکلات‌تان در راستای حل سوال را می‌توانید از بخش "سوال بپرسید" مطرح کنید.

راهنمایی ۱

در قدم اول، سوال را ساده می‌کنیم و فرض می‌کنیم که می‌خواهیم تابعی بنویسیم که یک میوه را در قالب یک کتابخانه ورودی می‌گیرد و بررسی می‌کند خوب است یا نه.

برای این کار تلاش می‌کنیم چنین تابعی را با توجه به شرایط سوال کامل کنیم:

def is_good(fruit):
    #returns true if the fruit is good
Python

اگر چنین تابعی را داشته باشیم می‌توانیم میوه‌ها را تک‌تک به آن بدهیم و بررسی کنیم خوب هستند یا نه.

راهنمایی ۲

تابع is_good را به شکل زیر پیاده‌سازی می‌کنیم

def is_good(fruit):
    if fruit['shape'] == 'sphere' and 300 <= fruit['mass'] <= 600 and 100 <= fruit['volume'] <= 500:
        return True # Fruit is good
    else:
        return False # Fruit is bad
Python

حالا باید در تابع خواسته شده در سوال تک به تک میوه ها رو با این روش بررسی کنیم تا ببینیم خوب هستند یا نه و لغت‌نامه‌مان را آپدیت کنیم:

def fruits(fruits_list):
    dict = {}
    for fruit in fruits_list:
        if is_good(fruit):
            # Update dictionary
    return dict
Python
راهنمایی ۳

حالا که هم میتونیم میوه‌های خوب و بد رو از هم تشخیص بدیم و تک تک میوه‌هامون رو هم بهش میدیم وقت این شده که لغت‌نامه‌مان را آپدیت کنیم!

برای اینکار اول چک میکنیم ببینیم میوه خوبمون وجود داره تو لغت‌نامه‌ یا نه اگر از قبل وجود داشتش به مقدارش یدونه اضافه میکنیم اگر هم وجود نداشت با مقدار یک به لغت‌نامه‌مان اضافه‌ش میکنیم!

در نهایت کدمون مثل کد پایین میشه:

def is_good(fruit):
    if fruit['shape'] == 'sphere' and 300 <= fruit['mass'] <= 600 and 100 <= fruit['volume'] <= 500:
        return True # Fruit is good
    else:
        return False # Fruit is bad

def fruits(fruits_list):
    dict = {}
    for fruit in fruits_list:
        if is_good(fruit):
        if fruit['name'] in dict:
            dict[fruit['name']] += 1
        else:
            dict[fruit['name']] = 1
    return dict
Python

موفق باشید :دی

chesstml



می‌خواهیم تابعی بنویسیم که تعداد لینک‌های یک فایل HTML را محاسبه کند.


کریم که نتوانست شطرنج فیزیکی خود را درست کند به اینترنت روی آورده و در این روزهای قرنطینه تصمیم گرفته که با استفاده از اینترنت و فضای آنلاین شطرنج بازی کند. در همان ابتدای استفاده از محیط اینترنت کریم عاشق شمردن تعداد لینک‌های صفحه‌ها شده و می‌خواهد برای صفحه داده شده تعداد لینک‌ها را بشمرد.

هدف ما این است که برنامه‌ای بنویسیم که کار او را راحت کند و با گرفتن یک فایل HTML تعداد لینک‌های آن صفحه را محاسبه کند.

جزئیات🔗

  • برنامه‌ی شما باید شامل یک تابع به نام ‍process ‌باشد.

  • تابع process به عنوان ورودی نام فایل HTML مورد نظر را دریافت می‌کند و تعداد لینک‌های این فایل را برمی‌گرداند (توجه کنید که فایل به صورت لوکال در کنار تست‌ها وجود دارد و شما باید آن را open کرده و اطلاعاتش را بخوانید).

  • لینک به معنای زنجیر است و برای هدایت کردن کاربران از صفحه جاری به صفحه‌ای دیگر استفاده می‌شود. به این صورت که متن مورد نظر خود را می نویسیم و کاری می‌کنیم که وقتی کاربران روی آن کلیک کردند به صفحه مورد نظر ما هدایت شوند.

  • برای ایجاد لینک در HTML از تگ a استفاده می‌کنیم به طور مثال:

<a href="https://quera.ir">کلیک کنید</a>
HTML

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

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

نکات🔗

  • نام فایل پایتون شما باید solution.py باشد.
  • تست‌های اصلی این سوال، صفحات اصلی سایت‌های مختلف هستند و برنامه شما باید توانایی کارکردن روی همه صفحات را داشته باشد.
  • می‌توانید فایل اولیه‌ی خام و کد تست‌ نمونه را با استفاده از این لینک دانلود کنید.
  • برای استفاده از کتابخانه‌های مختلف می‌توانید همراه فایل ارسالی، فایلی به نام python_requirements.txt بگذارید که در آن نام کتاب‌خانه‌های مورد نیاز و شمارهٔ نسخهٔ آن‌ها به فرمت زیر در آن موجود باشد: (اگر شماره نسخه را ننویسید آخرین نسخه آن کتاب‌خانه نصب می‌شود)
    firstlib==1.2.3
    secondlib==4.5.6
    ...
    Plain text

قسمت آموزشی🔗

در این قسمت راهنمایی‌های سوال به ترتیب در روزهای شنبه، دوشنبه و چهارشنبه ساعت ۱۸ اضافه می‌شود. مشکلات‌تان در راستای حل سوال را می‌توانید از بخش "سوال بپرسید" مطرح کنید.

راهنمایی ۱

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

ما استفاده از کتابخانه beautifulsoup را پیشنهاد می‌کنیم که یکی از کتابخانه‌های خوب برای حل این سوال است!

راهنمایی ۲

اول از همه کتابخانه BeautifulSoup را ایمپورت میکنیم.

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

from bs4 import BeautifulSoup

def process(path):
    with open(path) as html:
        soup = BeautifulSoup(html.read(), 'html')
        links = []
        # Find all links
        return len(links)
Python
راهنمایی ۳

در این مرحله باید تمام href ها رو از تگ‌های a از طریق کتابخانه BeautifulSoup پیدا کنیم که به روش زیر برای آن اقدام میکنیم:

for link in soup.find_all('a'):
    links.append(link.get('href'))
Python

و در نهایت کد ما به صورت زیر میشه:

from bs4 import BeautifulSoup

def process(path):
    with open(path) as html:
        soup = BeautifulSoup(html.read(), 'html')
        links = []
        for link in soup.find_all('a'):
            links.append(link.get('href'))
        return len(links)
Python

موفق باشید :دی

تولیدکننده‌ی شطرنجی



می‌خواهیم تابعی بنویسیم که بر روی تولید‌کننده‌ها (generator) پیمایش کند و به آن‌ها اطلاعاتی ارسال کند.


متاسفانه کریم اینترنتش قطع شده و با میوه‌هایش هم نتوانست مهره‌های شطرنج را بسازد و تصمیم گرفت بخوابد تا شاید زمان بگذرد. کریم خواب دید که مسئول یک مسابقه‌ی برنامه‌نویسی شده است.

او از شرکت‌کنندگان خواسته یک تولیدکننده یا generator بنویسند که عدد مطلوبی را حدس بزند؛ این تولیدکننده وضعیت (بزرگ‌تر، کوچک‌تر یا مساوی بودن) عدد حدسی، نسبت به عدد مطلوب را از پیمایش‌کننده خود دریافت می‌کند.

کریم به عنوان مسئول مسابقه باید این تولید‌کننده‌ها را بررسی کند، اما چون توانایی برنامه‌نویسی در کریم مشاهده نمی‌شود، از شما کمک می‌خواهد که تابعی به عنوان پیمایش‌کننده برای بررسی این تولیدکننده‌ها بنویسید.

پروژه اولیه🔗

پروژه اولیه را از اینجا دانلود کنید.

ساختار فایل‌های این پروژه به صورت زیر است.

guess-generator
├── generators.py
└── source.py
Plain text

جزئیات🔗

تولیدکننده‌ها انتظار دارند مقادیر زیر را از پیمایش‌کننده خود دریافت کنند:

  • G: عدد حدسی از عدد مطلوب بزرگ‌تر باشد.
  • L: عدد حدسی از عدد مطلوب کوچک‌تر باشد.
  • E: عدد حدسی با عدد مطلوب برابر باشد.

یک نمونه از تولیدکننده مورد نظر:

import random  

def guess_generator(min_value, max_value):  
    num = random.randint(min_value, max_value + 1)  
    resp = (yield num)  
    while resp != 'E':  # Equal  
        if resp == 'G':  # Greater  
            max_value = num - 1  
        elif resp == 'L':  # Less  
            min_value = num + 1  
        num = random.randint(min_value, max_value + 1)  
        resp = (yield num)  
Python

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

  • guess_generator: تولیدکننده‌ای که باید پیمایش شود.
  • min_value: حداقل مقدار برای حدس
  • max_value: حداکثر مقدار برای حدس
  • assumed_number: عدد فرضی برای حدس

و در خروجی یک لیست با شرایط زیر برگرداند:

  • لیست شامل تمام اعداد حدس زده شده توسط تولیدکننده به ترتیب پیمایش باشد.

  • بعد از هر حدس عجیب یک عضو ‍‍'!' باشد.

یک حدس عجیب است اگر یکی از شرایط زیر را داشته باشد:

  • اگر بعد از دریافت ‍‍G از طرف پیمایش‌گر عددی بزرگتر یا مساوی حدس قبلی حدس زده شود، این یک حدس عجیب است!

  • اگر بعد از دریافت ‍‍L از طرف پیمایش‌گر عددی کوچکتر یا مساوی حدس قبلی حدس زده شود، این یک حدس عجیب است!

  • اگر بعد از دریافت ‍‍E از طرف پیمایش‌گر حدس زدن عدد ادامه پیدا کند، این یک حدس عجیب است!

  • اگر عدد حدسی کمتر از min_value یا بیشتر از max_value باشد، این یک حدس عجیب است!

  • اگر در کل فرایند پیمایش سه مرتبه حدس عجیب اتفاق افتاد، یک عضو '!!!' به لیست خروجی اضافه شود و پیمایش متوقف شود.

برای مثال:

import generators  
from source import guess_generator_iterator  

gen = generators.guess_generator_1_correct  
min_value, max_value, num = 1, 100, 50  
lst = guess_generator_iterator(gen, min_value, max_value, num)  
print(lst)  

gen = generators.guess_generator_2_lazy  
min_value, max_value, num = 1, 10, 7  
lst = guess_generator_iterator(gen, min_value, max_value, num)  
print(lst)  

gen = generators.guess_generator_3_careless  
min_value, max_value, num = 1, 30, 15  
lst = guess_generator_iterator(gen, min_value, max_value, num)  
print(lst)  

gen = generators.guess_generator_4_stupid  
min_value, max_value, num = 1, 10, 5  
lst = guess_generator_iterator(gen, min_value, max_value, num)  
print(lst)  
Python

خروجی نمونه بالا:

[5, 15, 74, 71, 55, 49, 50]  
[1, 2, 3, 4, 5, 6, 7]  
[22, 2, 8, 9, 20, 18, 11, 15, 16, '!', 14, '!', 14, '!', '!!!']  
[4, 3, '!', 2, '!', 1, '!', '!!!']  
Python

در این مثال تولید‌کننده‌های اول و دوم می‌توانند به درستی عدد خواسته شده را حدس بزنند و هیچ حدس عجیبی هم نداریم؛ بنابراین حدس‌های تولیدکننده به ترتیب داخل یک لیست خروجی داده می‌شود. در دو مثال بعدی هر کدام از تولیدکننده‌ها سه حدس عجیب دارند که بعد از هر کدام از آن حدس‌ها یک '!' به لیست اضافه می‌شود و در آخر هم یک '!!!' به لیست اضافه می‌شود و حاصل برگردانده می‌شود.

نکات🔗

  • شما تنها مجاز به تغییر فایل ‍source.py و تکمیل تابع guess_generator_iterator هستید.
  • برای فرستادن کد صرفا فایل ‍source.py را با فرمت zip فشرده کرده و بفرستید.
  • می‌توانید کد تست‌ نمونه را با استفاده از این لینک دانلود کنید.

قسمت آموزشی🔗

در این قسمت راهنمایی‌های سوال به ترتیب در روزهای شنبه، دوشنبه و چهارشنبه ساعت ۱۸ اضافه می‌شود. مشکلات‌تان در راستای حل سوال را می‌توانید از بخش "سوال بپرسید" مطرح کنید.

راهنمایی ۱

در ابتدا برای حل این سوال شرط‌های گفته شده در سوال برای «حدس فرضی» داده شده رو بررسی کنید و حدس‌های بد و خوب رو مشخص و از هم جدا کنید.

راهنمایی ۲

نتیجه پیمایش تولیدکننده ورودی را درون یک متغیر قرار دهید. خروجی yield در تولیدکننده برابر مقداری است که شما از طریق تابع send متغیر ایجاد شده به تولیدکننده می‌فرستید. همین‌طور با استفاده از try و except می‌توانید پایان یافتن پیمایش تولیدکننده را مدیریت کنید.

در کل این سئوال به توانایی خواندن داکیومنت پایتون هم ربط دارد، پیشنهاد می‌کنیم برای یادگیری بهتر حتما لینک اول سوال را بخوانید.

راهنمایی ۳
numebr = 1

def myGenerator():
    global numebr
    while True:
        status = (yield numebr)
        if status == 'double':
            numebr *= 2
        else:
            numebr += 1

def myFunction():
    myGen = myGenerator()
    current = next(myGen)
    while True:
        try:
            print(current)
            if current > 100:
                myGen.close()
                break
            elif current%10 == 0:
                current = myGen.send('double')
            else:
                current = myGen.send('single')
        except:
            break

myFunction()   
Python

خروجی کد بالا به صورت زیر می‌باشد:

1
2
3
4
5
6
7
8
9
10
20
40
80
160
Plain text

Chess



می‌خواهیم تابعی بنویسیم که با استفاده از شیء گرایی یک شطرنج ساده پیاده‌سازی کند.


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

او از شما می‌خواهد برایش با استفاده از پایتون شطرنج را پیاده‌سازی کنید.

جزئیات🔗

  • این بازی در جدولی که از هر طرف نامتناهی‌ست انجام می‌شود. مهره‌ها به دو رنگ سفید و سیاه تقسیم شده‌اند و برای هر رنگ یک مهره‌ی شاه و به تعداد نامتنهای مهره‌ی سرباز که می‌توانند در صفحه حضور داشته باشند و می‌توانند هم حضور نداشته باشند داریم.

  • در ابتدای بازی مهره شاه سفید در خانه (10,10)(-10 , -10) و شاه سیاه در خانه (10,10)(10 , 10) قرار دارد. و سربازی درون بازی وجود ندارد.

  • نحوه‌ی پیروزی در این بازی به وسیله‌ی مات کردن حریف انجام می‌شود. مات کردن به این معنی‌ست که حداقل یکی از مهره‌های ما(به جز مهره‌ی شاه) در یکی از خانه‌های همسایه‌‌ی راسی(هشت خانه‌ی دور خانه‌ی مورد نظر) خانه‌ی شاه حریف باشد.

  • برای پیاده‌سازی باید از دو کلاس Piece و Board استفاده کنید که جزئیات آن را مشاهده می‌کنید:

کلاس Piece🔗

هر عضو این کلاس دارای سه ویژگی (Attribute) است که در تابع ــinitــ مقداردهی اولیه می‌شود و به ازای هر شی مقدار آن فرق دارد:

  • ۱. sort: که نشان دهنده‌ی نوع مهره‌ی مورد نظر می‌باشد. این مقدار یا "K" به معنای شاه و یا "P" به معنای سرباز است.
  • ۲. color: که نشان دهنده‌ی رنگ مهره‌ی مورد نظر می‌باشد. این مقدار یا "black" به معنای رنگ سیاه و یا "white" به معنای رنگ سفید است.
  • ۳. position: که نشان دهنده‌ی جایگاه مهره‌ی مورد نظر در صفحه می‌باشد به طور مثال این ویژگی برای مهره‌ی شاه سفید رنگ (10,10)(-10 , -10) می‌باشد. نوع این متغیر tuple است.

کلاس ‌Board🔗

هر عضو این کلاس دارای یک ویژگی (Attribute) است که در تابع ــinitــ مقداردهی اولیه می‌شود و به ازای هر شی مقدار آن فرق دارد:

  • position: که به صورت یک دیکشنری می‌باشد که قسمت Key در این دیکشنری به تمامی position های اشغال شده در صفحه تعلق دارد و قسمت Value برای هر position یک عنصر از کلاس Piece می‌باشد که در آن position جای گرفته است. توجه کنید که شما باید در تابع __init__ این کلاس شاه سفید و سیاه را با شرایطی که در بالا گفته شد به دیکشنری اضافه کنید.

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

  • ۱. add: این متد به عنوان ورودی یک عنصر از کلاس Piece را می‌گیرد و آن را به صفحه اضافه می‌کند. توجه کنید که تنها باید یک شاه از هر رنگ در صفحه وجود داشته باشد و در ‌Position مهره‌ای که به صفحه اضافه می‌کنیم نباید مهره‌ای وجود داشته باشد. اگر ورودی این متد در تناقض با این توضیحات بود متد باید عبارت "invalid query" را چاپ کند.

  • ۲. remove: این متد به عنوان ورودی یک position دریافت می‌کند و اگر در آن position مهره‌ای وجود داشت آن مهره را از صفحه پاک می‌کند. توجه کنید که حتما از هر رنگ دقیقا یک شاه باید درون صفحه‌ی بازی قرار داشته باشد. اگر ورودی این متد در تناقض با این توضیحات بود و یا در position ورودی مهره‌ای وجود نداشت متد باید عبارت "invalid query" را چاپ کند.

  • ۳. move: این متد به عنوان ورودی یک شی از نوع Piece و یک Position که نشان‌دهنده جایگاه جدیدی است که این مهره در آن باید قرار بگیرد، دریافت می‌کند. در صورتی که در جایگاه فعلی این مهره در صفحه، همین مهره وجود داشته باشد و جایگاه جدید نیز خالی از مهره باشد، این مهره را به آن جایگاه منتقل می‌کنیم. اگر در جایگاه فعلی این مهره در صفحه همین مهره وجود داشته باشد و جایگاه جدید یکی از مهره‌های سرباز حریف باشد، این مهره به آن جایگاه انتقال می‌یابد و مهره‌ی حریف از صفحه حذف می‌شود(چون حتما باید در هر لحظه از هر رنگ دقیقا یک مهره شاه وجود داشته باشد پس نمی‌تواند شاه حریف را مورد حمله قرار دهد). در غیر این صورت و یا اگر ورودی این متد در تناقض با این توضیحات بود متد باید عبارت "invalid query" را چاپ کند.

  • ۴. is_mate: این متد به عنوان ورودی یک رنگ ( "white" و یا "black") را دریافت می‌کند و بررسی می‌کند که آیا مهره‌های رنگ مورد نظر در وضعیت مات قرار دارد یا خیر. اگر قرار دارد مقدار True و اگر خیر مقدار False را برگرداند.

نکات🔗

  • نام فایل پایتون شما باید solution.pyباشد.
  • می‌توانید فایل اولیه‌ی خام و کد تست‌ نمونه را با استفاده از این لینک دانلود کنید.

قسمت آموزشی🔗

در این قسمت راهنمایی‌های سوال به ترتیب در روزهای شنبه، دوشنبه و چهارشنبه ساعت ۱۸ اضافه می‌شود. مشکلات‌تان در راستای حل سوال را می‌توانید از بخش "سوال بپرسید" مطرح کنید.

راهنمایی ۱

مقداردهی در تابع ــinitــ به این صورت می‌باشد که برای هر شیء مقادیر مشخص و متفاوتی می‌توانیم تعیین کنیم. به طور مثال نوشتن تابع ــinitــ برای کلاس Piece به صورت زیر می‌باشد:

def __init__(self, sort, color, position):
        self.color = color
        self.sort = sort
        self.position = position
Python

و یا برای نوشتن این تابع برای کلاس Board در ابتدا قبل از اضافه کردن شاه‌ها به صفحه چون ویژگی position یک دیکشنری خالی‌‌ست باید به صورت زیر باشد:

def __init__(self):
        self.position = {}
Python

برای متد add در کلاس Board باید چک کنیم در جایگاهی که می‌خواهیم مهره را add کنیم آیا مهره‌ای وجود دارد یا خیر اگر وجود دارد invalid query چاپ کنیم وگرنه در ویژگی position این Board باید این مهره اضافه شود.

برای متد remove در کلاس Board باید چک کنیم در این جایگاه مهره‌ای وجود دارد؟ و اگر وجود دارد این مهره شاه نباشد در غیر این صورت invalid query را چاپ کنیم در غیر این صورت مهره‌ای که در این جایگاه وجود دارد را از دیکشنری کلاس Board حذف کنیم.

برای متد move در کلاس Board باید چک کنیم اگر در position جدیدی که مهره باید به آنجا برود مهره‌ی همرنگ وجود داشت یا مهره‌ی شاه وجود داشت invalid query چاپ کند وگرنه اگر در جایگاه خالی‌ای می‌رود جایگاه الان مهره از دیکشنری Board حذف شود و جایگاه جدید مهره و این مهره در دیکشنری Board اضافه شوند و اگر در جایگاهی که می‌رود مهره‌ی حریف وجود داشت هم این مهره و جایگاهش و هم مهره‌ی حریف و جایگاهش از دیکشنری Board حذف شوند و مهره‌ی حرکت داده شده و جایگاه جدیدش در دیکشنری Board اضافه شوند.

برای متد is_mate در کلاس Board نیازمند به چک کردن هر هشت خانه‌ی دور شاه داریم تا بفهمیم که آیا حداقل یک مهره از مهره‌های سرباز حریف در این خانه‌ها وجود دارد یا خیر. دقت کنید اگر خانه‌ی شاه مورد نظر خانه‌ی (i,j)(i , j) هشت خانه‌ی دور آن خانه‌های زیر است:

(i+1,j)(i + 1 , j) , (i+1,j+1)(i + 1 , j + 1) , (i+1,j1)(i + 1, j - 1) , (i,j+1)(i , j + 1) , (i,j1)(i , j - 1) , (i1,j+1)(i - 1 , j + 1) , (i1,j)(i - 1, j) , (i1,j1)(i - 1, j - 1)

راهنمایی ۲

برای متد add در کلاس Boardشبه کد زیر همان ترجمه‌ی راهنمایی یک به زبان پایتون است:

if piece.sort=="K" or piece.position in self.position:
    print("invalid query")
else:
    self.position[piece.position]=piece
Plain text

برای متد remove در کلاس Board شبه کد زیر همان ترجمه‌ی راهنمایی یک به زبان پایتون است:

if position not in self.position:
    print("invalid query")
elif self.position[position].sort=="K":
    print("invalid query")
else:
    self.position.pop(position)
Plain text

برای متد move در کلاس Board در راهنمایی ۳ شبه کد قرار می‌گیرد.

برای متد is_mate در کلاس Board در راهنمایی ۳ شبه کد قرار می‌گیرد.

راهنمایی ۳

برای متد move در کلاس Board شبه کد زیر همان ترجمه‌ی راهنمایی یک به زبان پایتون است:

def move(self, piece, position2):
        if position2 in self.position:
            if self.position[position2].color == piece.color:
                print("invalid query")
            else:
                if self.position[position2].sort == "K":
                    print("invalid query")
                else:
                    del self.position[piece.position]
                    del self.position[position2]
                    self.position[position2] = piece
                    if piece.sort == "K":
                        if piece.color == "white":
                            global whitekingx, whitekingy, blackkingx, blackkingy
                            whitekingx = position2.x
                            whitekingy = position2.y
                        if piece.color == "black":  
                            blackkingx = position2.x
                            blackkingy = position2.y                  
        else:
            del self.position[piece.position]
            self.position[position2] = piece
Plain text

برای متد is_mate در کلاس Board شبه کد زیر همان ترجمه‌ی راهنمایی یک به زبان پایتون است:

def is_mate(self, color):
        if(color == "white"):
            for i in range(-1, 2):
                for j in range(-1, 2):
                    if i != 0 or j != 0:
                        position = (whitekingx + i, whitekingy + i)
                        if position in self.position:
                            return True
            return False
        else:
            for i in range(-1, 2):
                for j in range(-1, 2):
                    if i != 0 or j != 0:
                        if (blackkingx + i, blackkingy + i) in self.position:
                            return True
            return False
Plain text

پایرنج



می‌خواهیم کمک‌کننده‌ای (helper) بنویسیم که در موارد مشخص Exceptionها را لاگ کند و از آن بگذرد.


کریم در حال خواندن سوالات بود که از من به عنوان نویسنده‌ی سوالات بابت توهین‌هایی که به کریم کردم در نداشتن توانایی یاد گرفتن برنامه‌نویسی دلخور شد. برای همین سوالی را طرح کرد که به من بفهماند که من هم توانایی یاد گرفتن برنامه‌نویسی را ندارم. از شما می‌خو‌اهم به جای من سوال زیر را حل کنید.

جزئیات🔗

سوال از این قرار است که باید در پایتون کمک کننده‌ای به نام PyRanj پیاده‌سازی کنید به صورتی که این کمک‌کننده سه قابلیت اصلی زیر را داشته باشد:

۱. Wrapper🔗

هر گاه از pyranj به عنوان wrapper یک تابع استفاده شود، در تابع مورد نظر نباید هیچ تغییری ایجاد شود و فقط در زمان صدا زدن تابع، در صورتی که Exception پرتاب شود، صرفا باید این اتفاق لاگ شود و برنامه ادامه پیدا کند. برای مثال در کد زیر باید خروجی پایینی لاگ شود:

from pyranj import PyRanj as pyranj

@pyranj
def f():
    raise Exception('Ranj')

f()
Python
[EXCEPTION] :: Ranj
Plain text

۲. ContextManager🔗

هرگاه از pyranj به عنوان context استفاده شود، در عملیات مورد نظر نباید هیچ تغییری ایجاد شود و فقط اگر حین انجام عملیات، Exception پرتاب شود، صرفا باید این اتفاق لاگ شود و برنامه ادامه پیدا کند. برای مثال در کد زیر باید خروجی پایینی لاگ شود:

from pyranj import PyRanj as pyranj

with pyranj:
    raise Exception('Ranj')
Python
[EXCEPTION] :: Ranj
Plain text

۳. Mixin🔗

هرگاه از ‍pyranj در کلاسی ارث‌بری شود، در صورتی که آن کلاس دارای متد run باشد، تغییری که ایجاد می‌شود باید برابر با لاگ‌شدن Exception با فرمت مشخص شده باشد. برای مثال در کد زیر باید خروجی پایینی لاگ شود:

from pyranj import PyRanj as pyranj

class Runner(pyranj):
    def run(self):
        raise Exception('Ranj')

 Runner().run()
Python
[EXCEPTION] :: Ranj
Plain text

توجه: برای لاگ کردن باید از متد log شی logger به شیوه‌ی زیر عمل کنید و به آن یک رشته ورودی دهید (از اینجا می‌توانید نمونه logger که در تست‌ها استفاده می‌شود را دانلود کنید):

from pyranj import PyRanj as pyranj

from logger import logger

logger.log('PyRanj Log')
Python

علاوه بر قابلیت‌های اصلی، کمک‌کننده‌ی شما باید دارای قابلیت‌های زیر نیز باشد.

۱. تغییر دادن prefix در متن لاگ🔗

@pyranj(prefix='[PREFIX]')
def f():
    raise Exception('Ranj')

f()
Python
[PREFIX] :: Ranj
Plain text

توجه کنید که این نوع از تغییر prefix در تمام ویژگی‌های اصلی باید وجود داشته باشد.

۲. لغو کردن prefix در متن لاگ🔗

from pyranj import PyRanj as pyranj

with pyranj(prefix='[PREFIX]')():
    raise Exception('Ranj')
Python
[EXCEPTION] :: Ranj
Plain text

توجه: متنی که به ازای هر Exception لاگ می‌شود باید به فرمت زیر باشد، که در آن مقدار پیشفرض prefix، برابر است با [EXCEPTION].

f"{prefix} :: {exception}" 
Python

برای فهمیدن بهتر سوال می‌توانید مثال زیر و خروجی آن را مشاهده کنید.

from pyranj import PyRanj as pyranj

@pyranj
def f1():
    raise Exception('x1')

f1()

pyrannnnnnnj = pyranj()()(prefix='Hey')()

@pyrannnnnnnj(prefix='You')
def f2():
    raise Exception('x2')

f2()

with pyranj(prefix='Yes')()()()()():
    raise Exception('x3')


class A(pyranj):
    def run(self, num):
        raise Exception('x' * num)

A().run(5)


class B(pyranj()()(prefix='Hey there is an error')):
    def run(self):
        raise Exception('run ...')

B().run()
Python
[EXCEPTION] :: x1
You :: x2
[EXCEPTION] :: x3
[EXCEPTION] :: xxxxx
Hey there is an error :: run ...
Plain text

نکات🔗

  • نام برنامه‌ی ارسالی شما باید pyranj.py باشد که در آن شی PyRanj وجود داشته باشد و ویژگی‌های گفته شده را داشته باشد.
  • می‌توانید کد تست‌ نمونه را با استفاده از این لینک دانلود کنید.
  • تست‌های اصلی این سوال از ۵ بخش تشکیل شده که هر بخش دارای سه نوع تست است‌؛ یعنی هنگام ارسال شما نام هر متد تست را ۵ بارمی‌بینید. این‌ بخش‌ها به ترتیب دسته‌های زیر هستند:
    • بخش ‍‍BasePyRanjTest
    • بخش TestPyRanjInstantiation
    • بخش TestPyRanjInstantiationWithPrefix
    • بخش ‍‍TestPyRanjRecursiveInstantiation
    • بخش ‍‍TestPyRanjRecursiveInstantiationWithPrefix

قسمت آموزشی🔗

در این قسمت راهنمایی‌های سوال به ترتیب در روزهای شنبه، دوشنبه و چهارشنبه ساعت ۱۸ اضافه می‌شود. مشکلات‌تان در راستای حل سوال را می‌توانید از بخش "سوال بپرسید" مطرح کنید.

راهنمایی ۱

توصیه می‌شود ابتدا در مورد مفاهیم wrapper, contextManager, Mixin از این لینک‌ها بخوانید:

توجه کنید که helper مورد نظر لزومی ندارد به صورت عادی تعریف شود و می‌تواند خودش حاصل فراخوانی یک تابع دیگر باشد.

برای این که helper مورد نظر به طور همزمان قابل ارث‌بری باشد و هم‌چنین قابل فراخوانی، می‌توان از تابع __call__ در metaclass کمک گرفت. برای اطلاع ازین مورد می‌توانید لینک زیر را بخوانید:

راهنمایی ۲

ابتدا یک decorator عادی تعریف کنید که یک prefix ورودی می‌گیرد و یک تابع را تغییر می‌دهد.

سپس تابعی تعریف کنید به اسم get_pyranj که یک prefix هم ورودی می‌گیرد. اینگونه در هرجایی از class یا metaclass به آن prefix دسترسی دارید. درون تابع metaclass و class مربوطه را تعریف کنید و در خروجی آن class مورد نظر را برگردانید.

توابع enter و exit را در metaclass بیفزایید (در تابع exit می‌توانید بفهمید آیا در طول اجرا با این کانتکست Exception پرتاب شده است یا نه. حالا برای این‌که حالت ارث‌بری را به درستی هندل کنید، متود getattribute را در class باز نویسی کنید. و در صورتی که attribute مورد نظر اسمش برابر با run بود آن را دکوریت کنید.

اگر توجه داشته باشید موقع فراخوانی این helper سه حالت ممکن است پیش بیاید (ساخته شدن یک آبجکت جدید که از helper ارث بری کرده است، دکوریت کردن یک تابع، و تحویل گرفتن دکوریتور با یک prefix به عنوان ورودی) که می‌توان در متود call_ در metaclass آن‌ها را هندل کرد

def pyranj_message(e, prefix=None):
    ...


def pyranj_decorator(prefix=None):
    ...


def get_pyranj(prefix_=None):
    class InnerPyRanjMeta(type):
        def __call__(cls, *args, **kwargs):
            ...
            # We may call get_pyranj recursively

        def __enter__(self):
            pass

        def __exit__(self, exc_type, exc_val, exc_tb):
            ...

    class InnerPyRanj(metaclass=InnerPyRanjMeta):
        def __getattribute__(self, item):
            ...

    return InnerPyRanj


PyRanj = get_pyranj()
Python
راهنمایی ۳

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

from logger import logger

PREFIX = 'prefix'
FUNC = 'func'
RUN = 'run'


def pyranj_message(e, prefix=None):
    if prefix is None:
        prefix = '[EXCEPTION]'
    return f'{prefix} :: {e}'


def pyranj_decorator(prefix=None):
    def decorator(f):
        def wrapper(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except Exception as e:
                logger.log(pyranj_message(e, prefix))

        return wrapper

    return decorator


def get_pyranj(prefix_=None):
    class InnerPyRanjMeta(type):
        def __call__(cls, *args, **kwargs):
            if cls != InnerPyRanj:
                return super(InnerPyRanjMeta, cls).__call__(*args, **kwargs)
            param = args[0] if args else None
            func = param if type(param) == type(pyranj_decorator) else None
            prefix = kwargs.get(PREFIX, None)
            if func:
                return pyranj_decorator(prefix_)(func)
            return get_pyranj(prefix)

        def __enter__(self):
            pass

        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_type:
                logger.log(pyranj_message(exc_val, prefix=prefix_))
            return True

    class InnerPyRanj(metaclass=InnerPyRanjMeta):
        def __getattribute__(self, item):
            attr = super().__getattribute__(item)
            if item == RUN:
                return pyranj_decorator(prefix_)(attr)
            return attr

    return InnerPyRanj


PyRanj = get_pyranj()
Python