Skip to content

Commit

Permalink
Merge pull request #4 from Maxinger15/development
Browse files Browse the repository at this point in the history
Merging development to master
  • Loading branch information
Maxinger15 authored May 27, 2019
2 parents e702f0b + d005630 commit 9e90cae
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 37 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
lscreens.png
screen.png
34 changes: 30 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
Currently only for german and english.
Optimized for a 16:9 screens with a resolution of 1080x1920. If you have a different screen look in the "Issues" section for help.

This branch can be unstable.
---

<h2 id="only-for-android">ONLY FOR ANDROID</h1>
Expand All @@ -12,6 +14,8 @@ Optimized for a 16:9 screens with a resolution of 1080x1920. If you have a diffe
<li><a href="#config">Config</a></li>
<li><a href="#future">About the Future</a></li>
<li><a href="#help">Help me and others</a></li>
<li><a href="#screenTutorial">How to create a screen config</a></li>
<li><a href="https://www.reddit.com/r/PokemonRumbleRushBots/" target="_blank">Our Subreddit</a></li>
</ul>
<h2 id="what-it-does">What it does</h2>
<p>This Bot is able to play one of the three selected adventures and destroys every ore once your factory is full. This is good to get high level creatures.</p>
Expand All @@ -26,18 +30,18 @@ Optimized for a 16:9 screens with a resolution of 1080x1920. If you have a diffe
</ul>
<h3 id="python">Python:</h3>
<p>The main logic is written in python.<br>
<a href="https://www.python.org/downloads/">Download</a></p>
<a href="https://www.python.org/downloads/" target="_blank">Download</a></p>
<h3 id="tesseract">Tesseract:</h3>
<p>Tesseract is a optical character recognition engine which is used to detect the different states of the game by reading the text on your display.<br>
<a href="https://github.com/UB-Mannheim/tesseract/wiki">Download</a><br>
<a href="https://github.com/tesseract-ocr/tesseract">Wiki</a></p>
<a href="https://github.com/UB-Mannheim/tesseract/wiki" target="_blank">Download</a><br>
<a href="https://github.com/tesseract-ocr/tesseract" target="_blank">Wiki</a></p>
<h3 id="pure-python-adb">pure-python-adb</h3>
<p>This package is used to interact with the adb server.</p>
<h3 id="pytesseract">pytesseract</h3>
<p>This package is used to communicate with Tesseract</p>
<h3 id="minimal-adb-and-fastboot">Minimal adb and fastboot</h3>
<p>This is the host for the adb-server which is used to communicate with the mobile phone.<br>
<a href="https://forum.xda-developers.com/showthread.php?t=2317790">Download</a></p>
<a href="https://forum.xda-developers.com/showthread.php?t=2317790" target="_blank">Download</a></p>
<h2 id="installation">Installation</h2>
<h3 id="windows">Windows</h3>
<ol>
Expand Down Expand Up @@ -105,3 +109,25 @@ a long time). If you want to help me go to the project site on github and have a
The best thing you can do is to help me programming the bot. If you want to then write a message to me on discord (Maxinger#1608) or create a pull-request. If you have any
other idea how you can help me contact me. I am happy about every type of help. If you know how to fix common errors or something else about the bot feel free to write an article in the wiki</p>


<h3 id="screenTutorial">How to create a screen config</h3>
<ol>
<li>Start the script with <code>python start.py --init</code>. This will create a json with the name of your phone in the template folder inside the screens folder.</li>
<li>Go to the <a href="https://www.greenbot.com/article/2457986/how-to-enable-developer-options-on-your-android-phone-or-tablet.html" target="_blank">developer options</a> in your settings
and eneable the option "Pointer location". This will show you on the top of the screen a bar with the x and y coordinates of a touch on the display. If you have a notch you have to make a screenshot to see the y coordinate.</li>
<li>The following images show you the name in the config and the point on the screen where you have to take the coordinates:
<p><img src="https://raw.githubusercontent.com/Maxinger15/PokemonRumbleRushBot/images/adventure.png" width="400" height="400"/></p>
<p><img src="https://raw.githubusercontent.com/Maxinger15/PokemonRumbleRushBot/images/select_raid.png" width="400" height="500"/></p>
<p><img src="https://raw.githubusercontent.com/Maxinger15/PokemonRumbleRushBot/images/battle.png" width="400" height="400"/></p>
<p><img src="https://raw.githubusercontent.com/Maxinger15/PokemonRumbleRushBot/images/trashcan.png" width="400" height="400"/></p>
<p><img src="https://raw.githubusercontent.com/Maxinger15/PokemonRumbleRushBot/images/refineryClose.png" width="400" height="400"/> </p>
<p><img src="https://raw.githubusercontent.com/Maxinger15/PokemonRumbleRushBot/images/yesToRecycle.png" width="400" height="400"/> </p>
<p><img src="https://github.com/Maxinger15/PokemonRumbleRushBot/blob/images/dontRefine.png" width="400" height="400"/></p>
<p>The ore_acceptNoOre don`t need to be set at the begining. If you always have a ore working in your factory the message where this button is needed won`t apear.</p>
</li>
<li>Write your coordinates in the config. The first number is x the second is y</li>
<li>If you have used the --init argument to create the config the program recognize it automaticly.</li>
<li>If your config works commit it to the repo or post a link to the file to our <a href="https://www.reddit.com/r/PokemonRumbleRushBots/" target="_blank">subreddit</a></li>


</ol>
56 changes: 55 additions & 1 deletion bin/ADBScreen.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from PIL import Image
import os
import shutil


class ADBScreen:

def __init__(self, device):
self.device = device
self.screenfile = self.get_screenfile()

def get_screen(self) -> Image:
"""
Expand All @@ -22,4 +26,54 @@ def get_screen(self) -> Image:
return screens

def shell(self, command):
self.device.shell(str(command))
return self.device.shell(str(command))

def get_screen_size(self):
values = self.shell("dumpsys window displays | grep DisplayFrames")
values = values.strip(" ")
values = values.split(" ")

size = [-1, -1]
for string in values:
if "w=" in string:
size[0] = int(string.split("w=")[1])
if "h=" in string:
size[1] = (string.split("h=")[1])

if -1 in size:
print("Screen width can´t be detected. Please check the screen configuration and insert the values manually")
return values


def get_model_name(self):
print()
model = self.shell("getprop | grep 'ro.product.model'")
model = model.split(":")
model = model[1]
model = model.strip(" ")
model = model.strip('[')
model = model.rstrip()
model = model.strip("]")
model = model.replace(" ", "_")

return model
def get_screenfile(self):
dirname = os.path.dirname(__file__)
name = self.get_model_name()
return os.path.join(dirname, '../conf/screens/' + str(name + ".json"))


def create_model_template(self):
new_file = self.get_screenfile()
dirname = os.path.dirname(__file__)
new_file_dir = os.path.join(dirname, '../conf/screens/')
if os.path.isfile(new_file):
#raise ValueError("The config already exists. Please delete the config if you want to make a new one")
print("The config already exists. Please delete the config if you want to make a new one")
else:
src_file = os.path.join(dirname, '../conf/screens/template/template.json')
shutil.copy(src_file, new_file_dir)
os.rename(os.path.join(new_file_dir, "template.json"), new_file)

def fill_model_template(self):
raise NotImplementedError("Currently not implemented")
89 changes: 66 additions & 23 deletions bin/Player.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ def log(self, message):
if self.settings.debug:
print(str(message))



def get_coordinates(self, jsonname, layers=1, selected_layer= 0):
if layers == 1:
return str(self.settings.screen[jsonname][0])+" "+str(self.settings.screen[jsonname][1])
else: #row pos 1 array pos 2 array in 1 array
return str(self.settings.screen[jsonname][selected_layer][0])+" "+str(self.settings.screen[jsonname][selected_layer][1])


def check_string(self, string) -> bool:
Expand All @@ -44,13 +47,14 @@ def check_string(self, string) -> bool:
screens = self.adbscreen.get_screen()
sleep(3.5*self.settings.speed_multi)
area = (50, 840, 1078, 1918)
lscreens = screens.crop(area)
lscreens.save("lscreens.png")

# currently removed because of an size issue
#lscreens = screens.crop(area)
#lscreens.save("lscreens.png")
lscreens = screens
pytesseract.pytesseract.tesseract_cmd = self.settings.tesseract_dir
erg = pytesseract.image_to_string(lscreens).lower()
erg.rstrip("\n\r")
erg.replace(" ", "")
#erg = erg.rstrip("\n\r")
#erg = erg.replace(" ", "")
#self.log(erg.replace(" ", "")+" found by tesseract")
if string.lower() in erg:
# log("String: "+string+" ist in "+erg+" enthalten")
Expand All @@ -62,6 +66,7 @@ def check_string(self, string) -> bool:
newimdata = []
redcolor = (255, 10, 0, 255)
blackcolor = (0, 0, 0, 255)
whitecolor = (255, 255, 255, 255)
for color in lscreens.getdata():
if color[0] == 255 and color[0] == 255:
newimdata.append(blackcolor)
Expand All @@ -74,7 +79,23 @@ def check_string(self, string) -> bool:
#self.log("String: " + string + " ist in " + erg + " enthalten")
return True
else:
return False
# changes all whats not red into red and whats white to black. This
# could be better recognized by tesseract
# should help to improve the accuracy to detect the skip buttons
newimdata = []
for color in lscreens.getdata():
if color[0] == 255 and color[1] == 255 and color[2] == 255:
newimdata.append(blackcolor)
else:
newimdata.append(whitecolor)
new = Image.new(lscreens.mode, lscreens.size)
new.putdata(newimdata)
erg = pytesseract.image_to_string(new).lower()
if string.lower() in erg:
# self.log("String: " + string + " ist in " + erg + " enthalten")
return True
else:
return False


def check_end_of_fight(self, device) -> bool:
Expand All @@ -87,59 +108,75 @@ def grind(self):
adbscreen = self.adbscreen
# Tap the adventure button in the Main menue
self.log(" pressed adventure button")
adbscreen.shell("input tap 550 1670")
adbscreen.shell("input tap "+self.get_coordinates("adventurebutton"))
sleep(3.2*self.settings.speed_multi)
# Tap one of the three raids
self.log(" Select level")
if self.settings.selected_raid == "1":
adbscreen.shell("input tap 330 1150")
adbscreen.shell("input tap "+self.get_coordinates("select_raids", layers=3, selected_layer=0))
elif self.settings.selected_raid == "2":
adbscreen.shell("input tap 330 1312")
adbscreen.shell("input tap "+self.get_coordinates("select_raids", layers=3, selected_layer=1))
else:
adbscreen.shell("input tap 330 1463")
adbscreen.shell("input tap "+self.get_coordinates("select_raids", layers=3, selected_layer=2))
sleep(1.8*self.settings.speed_multi)
self.log(" Starting round")
#Tap the ready button
adbscreen.shell("input tap 559 1050")
#Tap the start button
adbscreen.shell("input tap "+self.get_coordinates("startbutton"))
count = 0
#This loop runs till the fight is finished
while not self.check_end_of_fight(adbscreen):
self.log(" Fight not finished")
if count == 8:
# Start the special attack
self.log(" starting special move")
adbscreen.shell("input tap 1000 1835")
adbscreen.shell("input tap "+self.get_coordinates("specialmovebtn"))
count = count + 1
sleep(4*self.settings.speed_multi)
sleep(2.5*self.settings.speed_multi)
self.log(" pressing forward buttons")

"""
foreward = True
#This loop runs while there are items displayed after the match
while self.check_string( self.settings.language_pack[0]) or foreward:
self.log(" forward....")
# Tap the result screen to continue
adbscreen.shell("input tap 559 1050")
adbscreen.shell("input tap "+self.get_coordinates("nextbutton"))
foreward = False
sleep(0.6*self.settings.speed_multi)
self.log(" finished forward buttons")
# This skips the last resultscreen
if self.check_string(self.settings.language_pack[1]):
self.log(" found finished button")
#Tap the last result screen to continue
adbscreen.shell("input tap 559 1050")
adbscreen.shell("input tap "+self.get_coordinates("donebutton"))
"""
for i in range(0, self.settings.taps_resultscreen):
adbscreen.shell("input tap " + self.get_coordinates("nextbutton"))
sleep(0.6)
sleep(4.9*self.settings.speed_multi)

sleep(4.8*self.settings.speed_multi)
#Looks if you have to many ores. If you have to many ores it deletes the new ore.
if self.check_string(self.settings.language_pack[2]):
self.log(" to many ores")
#Tap the delete button
adbscreen.shell("input tap 835 1476")
adbscreen.shell("input tap "+self.get_coordinates("ore_trashcanbutton"))
sleep(2*self.settings.speed_multi)
#Tap the yes button
adbscreen.shell("input tap 734 1067")
adbscreen.shell("input tap "+self.get_coordinates("ore_yesbutton"))
sleep(3.9*self.settings.speed_multi)
#Tap the quit button to go to main menue
adbscreen.shell("input tap 560 1790")
sleep(2.1*self.settings.speed_multi)
adbscreen.shell("input tap "+self.get_coordinates("ore_quitbutton"))
else:
# Tap the quit button to go to main menue
adbscreen.shell("input tap " + self.get_coordinates("ore_quitbutton"))

if(self.check_string(self.settings.language_pack[3])):
print("No ore in factory")
sleep(0.7 * self.settings.speed_multi)
# Presses the button on the infomessage if no ore is currently going to be refined
adbscreen.shell("input tap "+self.get_coordinates("ore_acceptNoOre"))
sleep(1.1 * self.settings.speed_multi)

self.log(" Finished")

def start(self):
Expand All @@ -148,3 +185,9 @@ def start(self):
self.log("Starting round " + str(i + 1))
self.grind()
sleep(2.5*self.settings.speed_multi)

def init(self):
self.adbscreen.create_model_template()

#Not implemented
#self.adbscreen.fill_model_template()
35 changes: 34 additions & 1 deletion bin/Settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
from pathlib import Path
import json
import os
from bin.ADBScreen import ADBScreen
from adb.client import Client as AdbClient

class Settings:
def __init__(self, ini_name):
#Loading config.cfg
cparser = configparser.ConfigParser()
dirname = os.path.dirname(__file__)
ini_file = Path(os.path.join(dirname, "..\conf\\"+ini_name))
Expand All @@ -21,6 +25,8 @@ def __init__(self, ini_name):
self.tesseract_dir = cparser.get("custom", "tesseract_directory")
self.rounds = cparser.getint("custom", "rounds")
self.debug = cparser.getboolean("custom", "debug")
self.autodetect_buttons = cparser.get("screen", "autodeteckt_buttons")
self.taps_resultscreen = cparser.getint("custom", "taps_resultscreen")
except Exception as e:
raise ValueError("Couldn´t read config file")

Expand All @@ -29,5 +35,32 @@ def __init__(self, ini_name):
data = json.load(json_file)
self.language_pack = data[self.language]
except Exception:
raise ValueError("Language not found. check your config file and update the languages.json")
raise ValueError("Language not found. Check your config file and update the languages.json")

client = AdbClient(host="127.0.0.1", port=5037)
devices = client.devices()
if len(devices) <= 0:
raise ValueError("No device connected")

adbscreen = ADBScreen(client.device(str(devices[0].get_serial_no())))
try:
with open(os.path.join(dirname, '../conf/screens/'+str(adbscreen.get_model_name()+".json"))) as json_file:
data = json.load(json_file)
self.screen = data
except Exception:
raise ValueError("Screenconfig not found. Start the python script with the --init argument to create a config.")

self.check_screen_values()

def check_screen_values(self):
for key, array in self.screen.items():
errmsg = "Not all coordinates are set correctly in your screen config. The values have to be greater than zero. The error ocured at the field " + str(key)
for nested in array:
if isinstance(nested, int):
if int(nested) < 0:

raise ValueError(errmsg)
else:
for nested2 in nested:
if int(nested2) < 0:
raise ValueError(errmsg)
19 changes: 19 additions & 0 deletions commandline_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

import argparse
from bin.Player import Player


def main(args):
player = Player()
player.adbscreen.get_screen_size()

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'-i',
'--init',
help="Creates a empty screen Template for your device",
action="store_true"
)
args = parser.parse_args()
main(args)
Loading

0 comments on commit 9e90cae

Please sign in to comment.