تابوت خندان! بیاید با کمک پایتون یک بات برای بازی تراوین بنویسیم.

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

اسم اکانتی که در بازی ترواین ساختم «LaughingCoffin» بود و طبیعتا اسم پروژه‌ی بات تراوین رو هم «LaughingCoffin» گذاشتم.
در این پست به دلایل اخلاقی نه کل کدهای اون بات بلکه ابزارهایی که ازشون برای نوشتن تابوت خندان استفاده کردم رو توضیح خواهم داد.


من ۷-۸ ساعت وقت گذاشتم و یک کد برای فارم کردن دهکده‌های اطراف نوشتم، قبل از این که وارد جزییات بشیم بیاید و خروجی تابوت خندان رو با هم ببینیم:
رتبه ۷اوم برترین مهاجمین کل بازی

رتبه ۹اوم برترین مهاجمین هفته سوم بازی
رتبه ۴اوم برترین مهاجمین هفته چهارم بازی


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

برای نوشتن بات تراوین به چه چیزهایی احتیاج داشتم؟

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

اول از همه زمان‌بندی‌ها:

اگر با یک زمان ثابت مثلا هر ۲ دقیقه یک حمله رو تدارک می‌دادم با توجه به این نکته که انسان‌ها  معمولا با یک فاصله‌ی زمانی ثابتی حمله نمی‌کنه حدس می‌زدم که ترواین متوجه تقلبم بشه.
از طرفی به نظرم استفاده از یک تابع رندم ساده هم گزیه‌ی مطلوبی نبود، به عنوان مثال شما یک حمله رو روی ۲ دقیقه و حمله‌ی بعدی رو روی ۹ دقیقه نمی‌زنید. (با فرض اینکه از یک تابع رندوم بین ۲ و ۱۰ استفاده کرده بودیم) پس لازم بود که یک تابع تولید اعداد تصادفی نا متقارن داشته باشیم که به عنوان مثال اگر فاصله‌ی بین دوتا حمله یک بازیکن معمولی ۱۷تا ۲۰ ثانیه هست این تابع اعداد بین این بازه رو با نرخ بیشتری تولید کنه.

برای ایجاد تاخیر با شرایط گفته شده در بالا از تابع np.random.choice استفاده کردم. این تابع یک پارامتر p داره که احتمال آمدن هر عدد رو مشخص می‌کنه.

برای مطالعه بیشتر در مورد تابع np.random.choice می‌تونید به +اینجا مراجعه کنید.

def random_sleep():
    delay_sec = [i for i in range(3,23)]
    delay_probability = [0.01, 0.01, 0.13, 0.08, 0.09, 0.12, 0.07, 0.11, 0.02, 0.04,
                         0.01, 0.04, 0.09, 0.04, 0.08, 0.01, 0.02, 0.01, 0.01, 0.01]
                  
    delay_time = np.random.choice(delay_sec, 1, p=delay_probability) + random.random()
    # print('sleep for {} sec'.format(delay_time))
    time.sleep(delay_time)

کلیک کردن:

برای کلیک کردن روی ساختمان‌ها و انتخاب تعداد سربازها و … از PyAutoGui استفاده کردم، خود سایت PyAutoGui راهنمای کاملی داره [+اینجا] و توی این پست صرفا به توابعی که از اون‌ها استفاده کردم اشاره می‌کنم.

با این تابع می‌شه روری مختصات دلخواه x,y کلیک چپ انجام داد:

pyautogui.click(x=_x, y=_y)

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

pyautogui.typewrite(str(_xy))

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

pyautogui.screenshot(dir_name + r'\data\sacrifice.png')

پیدا کردن محل اشیا مختلف:

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

def find_location(input_image_url, template_image_url):
    img = cv2.imread(input_image_url, 0)
    template = cv2.imread(template_image_url, 0)
    # Store width and heigth of template in w and h 
    w, h = template.shape[::-1] 
  
    # Perform match operations. 
    result = cv2.matchTemplate(img,template,cv2.TM_CCOEFF_NORMED) 
      
    # Specify a threshold 
    threshold = 0.9
      
    # Store the coordinates of matched area in a numpy array 
    loc = np.where( result >= threshold)  


    # Draw a rectangle around the matched region. 
    loctions = []

    for pt in zip(*loc[::-1]): 
        loctions.append((pt[0], pt[1]))
    if loctions:
        return(loctions[0])
    else:
        return('NotFound!')

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

ما تا اینجای کار می‌تونیم تاخیرهای شبیه رفتار انسان ایجاد کنیم، محل اشیا مختلف رو در تصویر پیدا کنیم و روی اون‌ها کلیک کنیم و برای وارد کردن مختصات دهکده‌ی هدف و غارت اون ابزار لازم رو داریم.

خروجی نهایی:

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

 

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

پ.ن: خیلی راه‌های بهتری هم برای نوشتن یک بات تراوین داریم اما هدف من یادگیری PyAutiGUI بود برای همین مسیر بالا رو رفتم.


نویسنده: محمد حسین سالاری.

موارد مشابه

پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *