Created
July 29, 2019 18:52
-
-
Save jsettlem/6ba2f666c3b6135f47ba7fcc58456ed9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import win32api | |
import win32con | |
import win32com.client | |
import time | |
import random | |
import win32gui | |
import pyscreenshot as ImageGrab | |
import cv2 | |
import cv2.cv as cv | |
import numpy | |
import math | |
import sys | |
from collections import Counter | |
import threading | |
import pygame as pg | |
''' | |
Block IDs: | |
-1 - empty | |
0 - unused | |
1 - blue | |
2 - yellow | |
3 - green | |
4 - red | |
5 - purple | |
6 - dragon | |
7 - pokeball | |
8 - garbage | |
''' | |
blocks = [[-1 for i in range(6)] for j in range(12)] | |
blocklock = False | |
mp = 'mp' in sys.argv | |
agro = 'agro' in sys.argv | |
''' | |
Tries to find the windows with "Pokemon Puzzle League" in the title | |
a = window x | |
b = window y | |
c = window width | |
d = window height | |
''' | |
a,b,c,d = 0,0,0,0 | |
def callback(hwnd, extra): | |
global a | |
global b | |
global c | |
global d | |
rect = win32gui.GetWindowRect(hwnd) | |
x = rect[0] | |
y = rect[1] | |
w = rect[2] | |
h = rect[3] | |
if "Pokemon Puzzle League" in win32gui.GetWindowText(hwnd): | |
a = x | |
b = y | |
c = w | |
d = h | |
win32gui.EnumWindows(callback, None) | |
cursor = cv2.imread('cursor.png') | |
electric = cv2.imread('electric.png') | |
fire = cv2.imread('fire.png') | |
grass = cv2.imread('grass.png') | |
heart = cv2.imread('heart.png') | |
water = cv2.imread('water.png') | |
dragon = cv2.imread('dragon.png') | |
pokeball = cv2.imread('pokeball.png') | |
poketypes = [water, electric, grass, fire, heart, dragon, pokeball] | |
#Keybindings in the Win32 API | |
VK_CODE = { | |
'A':0x5A, | |
'Z':0x58, | |
"Left":0x41, | |
"Right":0x44, | |
"Down":0x53, | |
"Up":0x57 | |
} | |
def spiral(N, M, x, y): #http://stackoverflow.com/questions/398299/looping-in-a-spiral/398302#398302 | |
dx, dy = 0, -1 | |
for dufdsafmb in xrange(N*M): | |
if abs(x) == abs(y) and [dx,dy] != [1,0] or x>0 and y == 1-x: | |
dx, dy = -dy, dx # corner, change direction | |
if abs(x)>N/2 or abs(y)>M/2: # non-square | |
dx, dy = -dy, dx # change direction | |
x, y = -y+dx, x+dy # jump | |
yield x, y | |
x, y = x+dx, y+dy | |
#Screenshot the board and locate the cursor | |
def findMii(): | |
global im | |
global open_cv_image | |
global coords | |
global yoffset | |
global curX | |
global curY | |
global blocklock | |
#Crop the image to the location of the field | |
if mp: | |
im = ImageGrab.grab(bbox=(a+3+43,b+44+55,222,392)).convert('RGB') | |
else: | |
im = ImageGrab.grab(bbox=(a+3+223,b+44+55,a+3+223+222,b+44+55+392)).convert('RGB') | |
open_cv_image = numpy.array(im) | |
#Convert RGB to BGR | |
coords = open_cv_image[:, :, ::-1].copy() | |
#Search for the cursor's x and y position | |
result = cv2.matchTemplate(coords,cursor,cv2.TM_CCOEFF_NORMED) | |
rawCurY, rawCurX = numpy.unravel_index(result.argmax(),result.shape) | |
#Determine if the cursor is big or small and use that to find the | |
#offset of the board | |
if(rawCurX in [0, 36, 72, 108, 144]): | |
yoffset = (rawCurY - 33) % 32 | |
rawCurY += 4 | |
else: | |
yoffset = (rawCurY - 37) % 32 | |
#Calculate the cursor's position on the grid | |
curX = int(math.floor(rawCurX / 36)) | |
curY = int(math.floor((rawCurY - 37) / 32)) + 1 | |
#press buttons | |
def press(*args): | |
''' | |
press, release | |
eg press('x', 'y', 'z') | |
''' | |
for i in args: | |
win32api.keybd_event(VK_CODE[i], 0, 0, 0) | |
time.sleep(0.05) | |
#Uncomment for fun debug | |
#print "Pressing: " + i | |
win32api.keybd_event(VK_CODE[i],0 ,win32con.KEYEVENTF_KEYUP ,0) | |
time.sleep(0.05) | |
#move the cursor to the specified row | |
def goToRow(row): | |
global curY | |
while row > curY: | |
curY += 1 | |
press("Down") | |
while row < curY: | |
curY -= 1 | |
press("Up") | |
#move the cursor to the specified column | |
def goToCol(col): | |
global curX | |
while col > curX: | |
curX += 1 | |
press("Right") | |
while col < curX: | |
curX -= 1 | |
press("Left") | |
#solve the row the cursor is currently on | |
def solveRow(): | |
print "TRYING TO SOLVE A ROW" | |
global curY | |
curRow = blocks[curY] | |
toSolve = Counter(curRow).most_common(1)[0][0] | |
targets = [i for i,val in enumerate(curRow) if val==toSolve] | |
if len(targets) > 2: | |
if targets[1] - targets[0] > 1: | |
for i in range(targets[1] - targets[0] - 1): | |
goToCol(targets[1]-1) | |
press("A") | |
targets[1] -= 1 | |
if targets[2] - targets[1] > 1: | |
for i in range(targets[2] - targets[1] - 1): | |
goToCol(targets[2]-1) | |
press("A") | |
targets[2] -= 1 | |
else: | |
pass | |
#go to row 'row' then move the block at block_col to col target | |
def resolveUpity(row, target, block_col): | |
goToRow(row) | |
targets = [target, block_col] | |
if targets[1] > targets[0]: | |
for i in range(int(math.fabs(targets[1] - targets[0]))): | |
goToCol(targets[1]-1) | |
press("A") | |
targets[1] -= 1 | |
#print "THE UPITY PROBLEM IS RESOLVED" | |
else: | |
for i in range(int(math.fabs(targets[0] - targets[1]))): | |
goToCol(targets[1]) | |
press("A") | |
targets[1] += 1 | |
#print "THE REVERSE UPITY PROBLEM IS RESOLVED" | |
#press buttons randomly and hope something good happens | |
def panic(): | |
for i in range(8): | |
press(random.choice(("Left","Right","Up","Down","Down"))) | |
press("A") | |
def main(): | |
global blocks | |
global blocklock | |
while True: | |
#reset the block list | |
blocks = [[-1 for i in range(6)] for j in range(12)] | |
#locate the cursor and populate 'coords' with the image of the field | |
findMii() | |
#It's thread safe! | |
#Or comment out this line to have the pygame display show the blocks as they're found | |
blocklock = True | |
for blocko in range(len(poketypes)): | |
#It won't search for pokeballs unless it's in multiplayer mode | |
if blocko != 6 or mp: | |
#finds all matches for the specified block above threshold of 0.999 | |
#TODO: Optimize the shit out of this. It searches much more than it needs to | |
result2 = cv2.matchTemplate(coords,poketypes[blocko],cv2.TM_CCOEFF_NORMED) | |
match_indices = numpy.arange(result2.size)[(result2>0.999).flatten()] | |
oak = numpy.unravel_index(match_indices,result2.shape) | |
blockYs = oak[0] | |
blockXs = oak[1] | |
#Add each block individually to the blocks list | |
for i in range(blockYs.size): | |
tempY = blockYs[i] - 25 - yoffset | |
tempX = blockXs[i] - 11 | |
tempX = int(math.floor(tempX / 36)) | |
tempY = int(math.floor((tempY + 37) / 32)) | |
blocks[tempY][tempX] = blocko + 1 | |
blocklock = False | |
#Get a copy of blocks[][] without empty cells | |
new_blocks = [[i for i in j if i not in (-1,0)] for j in blocks] | |
#Make an array listing the counts of the most common blocks in each row | |
row_counts = [Counter(i).most_common(1)[0][1] if len(i) > 0 else 0 for i in new_blocks] | |
#Find the row with the greatest number of blocks in common | |
best_row = row_counts.index(max(row_counts)) | |
#If there are a non-zero number of blocks: | |
if not cmp(blocks, [[-1 for i in range(6)] for j in range(12)]) == 0: | |
#If the blocks are high enough or agro is disabled | |
if not cmp(blocks[:6], [[-1 for i in range(6)] for j in range(6)]) == 0 or not agro: | |
if row_counts[best_row] > 2: | |
#BAD BAD BAD BAD BAD | |
goToRow(best_row) | |
solveRow() | |
else: | |
found = False | |
#Search out in a spiral from the cursor's position | |
#TODO: Make this less shit | |
for j,i in spiral(12,6,0,0): | |
j = min(max(j+curY,0),11) | |
i = min(max(i+curX,0),5) | |
if not found and blocks[j][i] not in (-1,0): | |
#Wreck it with an empty space to the right | |
if ((i < 5 and blocks[j][i+1] == -1 and blocks [j+1][i+1] == -1 and blocks [j+1][i] != -1 and blocks [j-1][i+1] == -1)): | |
print "I'M GONNA WRECK IT TO THE RIGHT!" | |
goToRow(j) | |
goToCol(i) | |
press("A") | |
found = True | |
#Wreck it with an empty space to the left | |
elif ((i > 0 and blocks[j][i-1] == -1 and blocks [j+1][i-1] == -1 and blocks [j+1][i] != -1 and blocks [j-1][i-1] == -1)): | |
print "I'M GONNA WRECK IT TO THE LEFT!" | |
goToRow(j) | |
goToCol(i-1) | |
press("A") | |
found = True | |
#Solve very specific 6-upity problem | |
elif j > 1 and j < 11 and i < 5 and blocks[j][i] in blocks[j-1] and blocks[j][i] in blocks[j+1] and blocks[j][i+1] in blocks[j-1] and blocks[j][i+1] in blocks[j+1] and -1 not in blocks[j] and -1 not in blocks[j-1] and blocks[j+1]: | |
print "SOLVING THE MAGICAL 6-UPITY PROBLEM" | |
resolveUpity(j-1, i+1, blocks[j-1].index(blocks[j][i])) | |
resolveUpity(j+1, i+1, blocks[j+1].index(blocks[j][i])) | |
resolveUpity(j-1, i, blocks[j-1].index(blocks[j][i+1])) | |
resolveUpity(j+1, i, blocks[j+1].index(blocks[j][i+1])) | |
goToRow(j) | |
goToCol(i) | |
press("A") | |
#Solve any 5-upity problem | |
elif j < 8 and blocks[j][i] in blocks[j-1] and blocks[j][i] in blocks[j+1] and blocks[j][i] in blocks[j+2] and blocks[j][i] in blocks[j+3]: | |
print "SOLVING THE GENERIC 5 UPITY PROBLEM" | |
resolveUpity(j-1, i, blocks[j-1].index(blocks[j][i])) | |
resolveUpity(j+2, i, blocks[j+2].index(blocks[j][i])) | |
resolveUpity(j+3, i, blocks[j+3].index(blocks[j][i])) | |
resolveUpity(j+1, i, blocks[j+1].index(blocks[j][i])) | |
found = True | |
#Solve the 4-upity problem with 2 adjacent | |
elif j < 9 and blocks[j-1][i] == blocks[j][i] and blocks[j][i] in blocks[j+1] and blocks[j][i] in blocks[j+2]: | |
print "SOLVING A SPECIFIC 4-UPITY PROBLEM" | |
resolveUpity(j+2, i, blocks[j+2].index(blocks[j][i])) | |
resolveUpity(j+1, i, blocks[j+1].index(blocks[j][i])) | |
found = True | |
elif j > 1 and blocks[j+1][i] == blocks[j][i] and blocks[j][i] in blocks[j-1] and blocks[j][i] in blocks[j-2]:#DEATH | |
print "SOLVING A SPECIFIC 4-UPITY PROBLEM" | |
resolveUpity(j-2, i, blocks[j-2].index(blocks[j][i])) | |
resolveUpity(j-1, i, blocks[j-1].index(blocks[j][i])) | |
found = True | |
#Solve any 4-upity problem | |
elif j < 9 and blocks[j][i] in blocks[j-1] and blocks[j][i] in blocks[j+1] and blocks[j][i] in blocks[j+2]: | |
print "SOLVING THE GENERIC 4 UPITY PROBLEM" | |
resolveUpity(j+2, i, blocks[j+2].index(blocks[j][i])) | |
resolveUpity(j-1, i, blocks[j-1].index(blocks[j][i])) | |
resolveUpity(j+1, i, blocks[j+1].index(blocks[j][i])) | |
found = True | |
#Solve simple 3-upity | |
elif blocks[j-1][i] == blocks[j][i] and blocks[j][i] in blocks[j+1]: | |
resolveUpity(j+1, i, blocks[j+1].index(blocks[j][i])) | |
found = True | |
elif blocks[j+1][i] == blocks[j][i] and blocks[j][i] in blocks[j-1]: | |
resolveUpity(j-1, i, blocks[j-1].index(blocks[j][i])) | |
found = True | |
elif ((j < 10) and (blocks[j+2][i] == blocks[j][i]) and (blocks[j][i] in blocks[j+1])): | |
resolveUpity(j+1, i, blocks[j+1].index(blocks[j][i])) | |
found = True | |
#Get desperate, solve any 3-upity problem | |
elif j > 3 and blocks[j][i] in blocks[j-1] and blocks[j][i] in blocks[j-2]: | |
print "GETTING DESPERATE, WHORING BODY OUT TO 3-UPITY PROBLEM" | |
resolveUpity(j-1, i, blocks[j-1].index(blocks[j][i])) | |
resolveUpity(j-2, i, blocks[j-2].index(blocks[j][i])) | |
found = True | |
if not found: | |
panic() | |
print "ANARCHY OR RIOT" | |
else: | |
#If agro is enabled and the tower is too short | |
press("Z") | |
print "TOO EASY" | |
else: | |
#If no blocks can be found, try to "navigate" the menus | |
press("A") | |
#Uses pygame to make the pretty output window | |
def output(): | |
pg.init() | |
fps = pg.time.Clock() | |
window = pg.display.set_mode((20*6,23*12)) | |
pg.display.set_caption("PPL Output") | |
pgCursor = pg.image.load('cursor.png') | |
pgElectric = pg.image.load('electric.png') | |
pgFire = pg.image.load('fire.png') | |
pgGrass = pg.image.load('grass.png') | |
pgHeart = pg.image.load('heart.png') | |
pgWater = pg.image.load('water.png') | |
pgDragon = pg.image.load('dragon.png') | |
pgPokeball = pg.image.load('pokeball.png') | |
pgpoketypes = [pgWater, pgElectric, pgGrass, pgFire, pgHeart, pgDragon, pgPokeball] | |
while True: | |
if not blocklock: | |
window.fill(pg.Color(0,0,0)) | |
for i in range(12): | |
for j in range(6): | |
if blocks[i][j] in range(1,8): | |
window.blit(pgpoketypes[blocks[i][j]-1], (j*20,i*23)) | |
for event in pg.event.get(): | |
if event.type == pg.QUIT: | |
pg.quit() | |
sys.exit() | |
pg.display.update() | |
fps.tick(60) | |
main = threading.Thread(target=main) | |
main.dameon = True | |
main.start() | |
out = threading.Thread(target=output) | |
out.dameon = True | |
out.start() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment