Есть такая игра «Индикот» для Android. Игра как игра, казуального жанра «три в ряд», коих сейчас вагон. Но вот как-то так получилось, что играем мы в неё третий год, и всё никак не доиграем.
Как и в остальных играх модели «free-to-play», здесь предлагается приобретать игровые бонусы за местную валюту — бантики. Виртуальные бантики можно купить за вполне себе реальные рубли, а можно заработать просмотром однообразной рекламы — периодически и понемногу.
Каждые полчаса появляется возможность получить 10 бантиков за один просмотр рекламного ролика. Звучит как отличный повод для автоматизации! Стало быть, нужно создать бота, который запустит игру, подождёт полчаса, нажмёт правильные кнопочки для запуска ролика и терпеливо посмотрит рекламу. В конце концов, кого волнует, что рекламодатель заплатил настоящих денег, чтоб его рекламу крутили? Пусть скажет спасибо, что мы хотя бы её не скликиваем.
Итак, нам нужно как-то получить скриншот для анализа с Android-аппарата. Воспользуемся утилитой ADB из комплекта Android SDK. В аппарате предварительно нужно активировать developer-режим.
Сделать скриншот можно стандартной Android-утилитой screencap
. Работает даже без root’а!
adb shell "screencap -p" | sed 's/\r$//' > screenshot.png
Загвоздка в том, что создание PNG-картинки на аппарате может занять продолжительное время. Поэтому я сделал примерно вот так (нужен установленный ImageMagick):
adb shell "screencap | gzip" | \
sed 's/\r$//' | \
gunzip | \
tail -c +13 | \
convert -size 2560x1600 -depth 8 rgba:- screenshot.png
Скриншот снимаем в raw-виде, сжимаем на аппарате, разжимаем на ноутбуке и там же конвертируем в PNG. Получается чуть быстрее, но всё равно медленно: от одной до семи секунд. Пока непонятно, как бы ускорить процесс, и это останавливает от написания полноценного бота, который бы самостоятельно проходил уровни. Впрочем, такой скорости достаточно для нашей задачи.
Экраны игры можно представить как состояния конечного автомата. Надо наделать эталонных скриншотов в этих состояниях. Имея эталоны, можно сравнивать текущий скриншот с ними и определять, на каком экране мы находимся. Ну и соответствующим образом переходить в следующий.
Картинки легко сравнивать с помощью наложения в режиме смешивания «разница». Если некоторый регион на картинках одинаковый, результирующий регион будет абсолютно чёрным.Есть чёрный регион — есть совпадение.
Определив, на каком экране мы находимся, мы можем нажимать правильные кнопки на экране. На помощь опять приходит ADB. Так мы сэмулируем клик по координатам (100, 100):
adb shell input tap 100 100
А вот так — нажатие «железной» кнопки вкл/выкл:
adb shell input keyevent 26
Ещё надо добавить немного логики: если два последовательных скриншота одинаковые, и имеют специальный логотип, значит, реклама закончилась, можно возвращаться в игру. Или: если попали на экран с таймером, надо выключить экран и подождать полчаса, пока таймер не обнулится, и мы сможем снова смотреть вожделённую рекламу.
Я собрал небольшой скрипт на Python: репозиторий. Теперь остаётся запустить его и, откинувшись в кресле, наблюдать, как растёт бантичное благосостояние…
Обожаю вас, Михаил! Теперь мы сказочно богаты! Можно тратить бантики налево и направо 🙂