Последние 10 лет я играл в такие игры, как TownsMen 6, Clash of the Clans, SimCity и мою любимую OpenTTD (с открытым исходным кодом!).

Попробовав City Island 5, я был раздражен от того, что предметы не накапливались, пока я находился вне игры. У меня может быть самый лучший бизнес, стратегия и т.д., но я должен быть в игре, чтобы обеспечить сбор денег/ключей/золота с течением времени. Например, если моя пекарня зарабатывает 100 евро в минуту, я заработаю 100 евро только после того, как выйду из игры и вернусь через 24 часа.

Это стало особенно утомительным, когда я пытался накопить €5 000 000, необходимых для покупки острова, показанного ниже. Это займет у меня примерно две недели игры, если я не буду тратить деньги - оно того не стоит!

Создание скрипта Python для сбора ценностей для меня

Это проблема, которую можно решить с помощью машинного обучения.

a. Захват фреймов игры

Мне нужен был способ захвата фреймов игры в реальном времени.

Проще всего сделать снимок экрана в игре и передать его на следующие шаги сценария.

Для создания скриншота я использую библиотеку Python MSS. Это простая библиотека, которая позволяет захватить экран и сохранить его в файл. Мы также можем использовать библиотеку для выбора монитора и получения его свойств, таких как ширина и высота.

Мы будем использовать OpenCv (cv2) для части сценария, связанной с компьютерным зрением. Это библиотека, которая позволяет нам выполнять задачи обработки изображений и компьютерного зрения. Здесь мы используем метод cv2.imread() для загрузки изображения из указанного файла.

import cv2 
import mss

sct = mss.mss() 
default_monitor = sct.monitors[1]

def click_template_image(monitor=default_monitor):

    # Screenshot
    game_screenshot_path = "sct_{width}x{height}.png".format(**monitor)
    sct_img = sct.grab((0, 0, monitor["width"], monitor["height"]))
    mss.tools.to_png(sct_img.rgb, sct_img.size, output=game_screenshot_path)

    game_screenshot = cv2.imread(game_screenshot_path, 1)

b. Распознаем ресурсы на скриншоте

Нам нужен способ обнаружить ресурсы игры и затем вернуть их координаты.

Алгоритмы OpenCv TemplateMatching идеально подходят для этого.

Они используются для поиска и определения местоположения шаблонного изображения (например, ценного предмета) в большом изображении (например, в фиде игры). OpenCV просто накладывает изображение шаблона на входное изображение (как в 2D-свертке) и сравнивает шаблон и участок входного изображения под изображением шаблона. В OpenCV реализовано несколько методов сравнения. (Более подробную информацию вы можете найти в документации). Мы используем его в методе: cv2.matchTemplate(... ).

Для достижения этой цели мне понадобились изображения шаблонов. Я сделал скриншоты вручную, а затем обрезал кеш, звезду и ключ:


В приведенном ниже примере кода мы распознаем кеш.

import cv2
import mss
import numpy as np

sct = mss.mss()
default_monitor = sct.monitors[1]

def click_template_image(monitor=default_monitor):

    # 1. Screenshot
    game_screenshot_path = "sct_{width}x{height}.png".format(**monitor)
    sct_img = sct.grab((0, 0, monitor["width"], monitor["height"]))
    mss.tools.to_png(sct_img.rgb, sct_img.size, output=game_screenshot_path)
    game_screenshot = cv2.imread(game_screenshot_path, 1)


    # 2. Find a way to identify the valuables in the screenshot
    template_image = cv2.imread("images/cash.png", 1)

    search_result = cv2.matchTemplate(game_screenshot, template_image, cv2.TM_CCOEFF_NORMED)

    y_coords, x_coords = np.where(search_result >= threshold)

    for idx in range(len(x_coords)):
        x, y = x_coords[idx], y_coords[idx]

c. Собираем ресурсы кликом

Получив координаты элемента, мы должны кликнуть по нему.

Функция pyautogui.click(x,y) работает для этого замечательно. Она щелкает по экрану по координатам x,y. Подробнее о ней можно узнать здесь.

Примечание:

  • Мы выбираем координаты, которые соответствуют определенному порогу доверия. Показатель уверенности - это число от 0 до 1, которое представляет собой вероятность того, что результат модели верен и удовлетворит запрос пользователя. Например, мы можем отобрать координаты, уровень доверия к которым составляет 0,7 или выше. Именно для этого мы и используем пороговую переменную. Алгоритм matchTemplate() дает нам несколько точек на карте, которые соответствуют нашему запросу. Затем я решил отфильтровать точки, которые находятся ниже порога: y_coords, x_coords = np.where(search_result >= threshold).

  • После нескольких проб я понял, что многократное нажатие на карту за один запуск алгоритма приводит к ошибкам и неточностям. Например, прежде чем щелкнуть на движущемся автомобиле, он мог немного сдвинуться с места. Я решил поэкспериментировать с количеством щелчков при каждом вызове функции click_template_image() с помощью переменной number_of_clicks и остановился на одном щелчке за шаг.

  • Я обнаружил, что щелчок по центру изображения работает лучше, чем щелчок по левому верхнему краю, то есть по координатам, которые нам дала наша функция подбора шаблона. Мы можем использовать высоту и ширину изображения шаблона для вычисления координат центра: x_c = int((x + x + w) // 2) & y_c = int((y + y + h) // 2)

import cv2
import mss
import numpy as np
import pyautogui

pyautogui.FAILSAFE = False

sct = mss.mss()
default_monitor = sct.monitors[1]

def click_template_image(monitor=default_monitor, number_of_clicks=1, threshold=0.7):

    # 1. Screenshot
    game_screenshot_path = "sct_{width}x{height}.png".format(**monitor)
    sct_img = sct.grab((0, 0, monitor["width"], monitor["height"]))
    mss.tools.to_png(sct_img.rgb, sct_img.size, output=game_screenshot_path)
    game_screenshot = cv2.imread(game_screenshot_path, 1)

    # 2. Find a way to identify the valuables in the screenshot
    template_image = cv2.imread("images/cash.png", 1)
    search_result = cv2.matchTemplate(game_screenshot, template_image, cv2.TM_CCOEFF_NORMED)
    y_coords, x_coords = np.where(search_result >= threshold)

    # get the width and height of the template image
    w, h = template_image.shape[1], template_image.shape[0]
    for idx in range(number_of_clicks):
        if idx + 1 > len(x_coords):
            continue
        x, y = x_coords[idx], y_coords[idx]

        # 3. Collect the valuables by clicking on them
        # get centres
        x_c = int((x + x + w) // 2)
        y_c = int((y + y + h) // 2)

        pyautogui.click(x=x_c, y=y_c)

d. Закрываем всплывающие окна

Наши нажатия выше могут привести к появлению всплывающих окон, когда мы получаем награду, повышаем уровень и т.д.

Нам нужно закрыть окно, прежде чем снова попытаться собрать ценности. Мы используем ту же логику, что и при поиске и нажатии на ценные вещи.

Для этого мне понадобились шаблонные изображения для кнопок закрытия всплывающих окон, чтобы их можно было нажать. Я сделал скриншоты вручную, а затем обрезал различные кнопки закрытия:

Для закрытия я использовал тот же код, что выше.

Результаты после запуска на ночь

Я начал игру с €316,415 в кармане.

На следующее утро у меня было €6,463,870.

И я смог купить тот остров, что я хотел

В заключение

Вообще-то это называется использовать читы, но так ли это плохо в данном случае?

Полный код автора доступен по ссылке.

Еще больше примеров использования ML в современных сервисах можно посмотреть в моем телеграм канале. Я пишу про ML, стартапы и релокацию в UK для IT специалистов.

Комментарии (12)


  1. holodoz
    30.12.2022 12:02
    +3

    Я ничего не знал о подобных библиотеках, знал только питон. С chatgpt написал похожее приложение за пару часов. Напильником, конечно дорабатывать порядочно, но остов приложения и общее направление ИИ задал верно


    1. fire64
      30.12.2022 12:04
      +5

      вот как раз то, о чем говорили.

      Chatgpt начал писать людям приложения.


      1. Aniro
        30.12.2022 13:36
        +2

        Пока не начал писать, а начал помогать писать. И мне кажется копилот справляется с этим лучше. Чатик очень часто мыслит в верном направлении, но его решение не работает из-за какой-нибудь мелочи. Доработка же требует понимания даже больше чем написание с нуля.


  1. mcksin
    30.12.2022 12:15
    +18

    Когда давно была похожая статья, человеку было лень выполнять дейлики и прочие активности по таймеру в мобильной гриндилке и решил это дело автоматизировать, в итоге ему было интереснее автоматизировать чем играть , игра была заброшена


    1. Yamazaki123
      30.12.2022 14:19
      +2

      У меня с тетрисом такая же история. Пилить алгоритм, который сам раскладывает фигурки нужным образом, оказалось несопоставимо увлекательнее, чем делать это самому. В планах сделать прямо в стене дома экран из 10*20 (стандартный размер стакана в тетрисе) стеклоблоков, каждый подсветить адресуемыми светодиодами, и с малинки гнать туда изображение.


    1. omican
      30.12.2022 21:28

      У меня такое не раз было. Писал много скриптов, включая доморощенный OCR, для Diablo II, нескольких браузерок, Path of Exile. Когда оно начинает работать, интерес резко падает. Про попиксельный OCR с помощью Autohotkey для Diablo даже думал статью написать (рабочее название: "Крафт с помощью OCR для Diablo II или как выпить 400 зелий менее чем за секунду"), но не дошли руки.


  1. DarkPreacher
    30.12.2022 12:46
    +2

    Очень давно делал аналогичное для Gemcraft, только с использованием AutoHotKey.


    1. NiPh
      30.12.2022 13:18
      +1

      Причем хватает определения цвета одного-двух пикселей по координатам + выравнивание размера окна эмулятора на старте, конкретно для такого типа игр.


      1. DarkPreacher
        30.12.2022 13:43
        +1

        В принципе да, однако я и по части изображения делал распознавание, в противном случае немного сложно определить когда уровень закончился, или какие-то специфичные вещи.


  1. Murtusa
    30.12.2022 13:38
    +13

    Это можно сделать и через обычный блокнот и приложении Game Guardian , написать скрипты и накрутить много ресурсов , научил сына к таким вещам и отбил страсть к играм , у него только цель начать игру , взломать , после достижения цели берёт паузу ))) ребёнку 8 лет, умеет вытаскивать зашифрованные данные , декампилировать файл и наоборот, хобби хакер


    1. Arty_Fact
      30.12.2022 14:39
      +1

      Если я правильно понимаю, большинство гриндилок как на экране выше, хранят данные на сервере и простым взломом не обойтись.
      Научите и меня, я так скрипты в GG и не научился писать, только стандартным поиском пользуюсь.


  1. vkflare
    01.01.2023 11:59

    Было бы интересно почитать и порассуждать про следующую ступень эволюции - dll инжектор, смотрящий в память.