Последние 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)
mcksin
30.12.2022 12:15+18Когда давно была похожая статья, человеку было лень выполнять дейлики и прочие активности по таймеру в мобильной гриндилке и решил это дело автоматизировать, в итоге ему было интереснее автоматизировать чем играть , игра была заброшена
Yamazaki123
30.12.2022 14:19+2У меня с тетрисом такая же история. Пилить алгоритм, который сам раскладывает фигурки нужным образом, оказалось несопоставимо увлекательнее, чем делать это самому. В планах сделать прямо в стене дома экран из 10*20 (стандартный размер стакана в тетрисе) стеклоблоков, каждый подсветить адресуемыми светодиодами, и с малинки гнать туда изображение.
omican
30.12.2022 21:28У меня такое не раз было. Писал много скриптов, включая доморощенный OCR, для Diablo II, нескольких браузерок, Path of Exile. Когда оно начинает работать, интерес резко падает. Про попиксельный OCR с помощью Autohotkey для Diablo даже думал статью написать (рабочее название: "Крафт с помощью OCR для Diablo II или как выпить 400 зелий менее чем за секунду"), но не дошли руки.
DarkPreacher
30.12.2022 12:46+2Очень давно делал аналогичное для Gemcraft, только с использованием AutoHotKey.
NiPh
30.12.2022 13:18+1Причем хватает определения цвета одного-двух пикселей по координатам + выравнивание размера окна эмулятора на старте, конкретно для такого типа игр.
DarkPreacher
30.12.2022 13:43+1В принципе да, однако я и по части изображения делал распознавание, в противном случае немного сложно определить когда уровень закончился, или какие-то специфичные вещи.
Murtusa
30.12.2022 13:38+13Это можно сделать и через обычный блокнот и приложении Game Guardian , написать скрипты и накрутить много ресурсов , научил сына к таким вещам и отбил страсть к играм , у него только цель начать игру , взломать , после достижения цели берёт паузу ))) ребёнку 8 лет, умеет вытаскивать зашифрованные данные , декампилировать файл и наоборот, хобби хакер
Arty_Fact
30.12.2022 14:39+1Если я правильно понимаю, большинство гриндилок как на экране выше, хранят данные на сервере и простым взломом не обойтись.
Научите и меня, я так скрипты в GG и не научился писать, только стандартным поиском пользуюсь.
vkflare
01.01.2023 11:59Было бы интересно почитать и порассуждать про следующую ступень эволюции - dll инжектор, смотрящий в память.
holodoz
Я ничего не знал о подобных библиотеках, знал только питон. С chatgpt написал похожее приложение за пару часов. Напильником, конечно дорабатывать порядочно, но остов приложения и общее направление ИИ задал верно
fire64
вот как раз то, о чем говорили.
Chatgpt начал писать людям приложения.
Aniro
Пока не начал писать, а начал помогать писать. И мне кажется копилот справляется с этим лучше. Чатик очень часто мыслит в верном направлении, но его решение не работает из-за какой-нибудь мелочи. Доработка же требует понимания даже больше чем написание с нуля.