Последние 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 специалистов.