python制作连连看外挂
前几天看到github上的用python写的连连看外挂,最近几天一直在琢磨这个事情,昨天晚上弄到凌晨两点,终于把程序全部调通了,其中的关键部分算法全部重新写了一遍,其实原理非常简单。程序启动时,会自动从屏幕中进行截屏,找到连连看游戏窗口的起始坐标,根据坐标偏移,然后找到连连看的游戏区域.连连看最多可以放置的图片为19*11张。
按照约定图片的大小,游戏区域全部切分为31*35的小图片,然后将小图片按照不同的类别进行标记,具体的标记算法可以看源代码。我们设定empty背景为基准图片,然后计算所有图片与基准图片的像素之差的曼哈顿距离之和,按照距离的数值进行类别划分,且我们标记empty图片的类别为0。
将图片转换为标记矩阵后,我们就可以根据我们之前所知道的识别算法,来找到矩阵中的所有配对的坐标,然后驱动鼠标进行点击连接即可。
视频如下:
源代码: github地址:
import sys, time
import math, random
import cv2
import win32api
import win32gui
import win32con
from PIL import ImageGrab
import time
WINDOW_TITLE = "QQ游戏 - 连连看角色版"
MARGIN_LEFT = 14
MARGIN_HEIGHT = 181
COL_NUM = 19
ROW_NUM = 11
SQUARE_WIDTH = 31
SQUARE_HEIGHT = 35
SUB_LT_X = 4
SUB_LT_Y = 4
SUB_RB_X = 25
SUB_RB_Y = 29
CLICK_POS_X = 15
CLICK_POS_Y = 18
def TIME_INTERVAL():
return 0.001
#return 0.375+random.uniform(-0.075, 0.075)+0.13*progress
def TIME_INTERVAL_1():
return 0.001
#return 0.375+random.uniform(-0.075, 0.075)
def getGameWindowPosition():
window = win32gui.FindWindow(None, WINDOW_TITLE)
while not window:
print('unable to find window, try in 3 secs...')
time.sleep(3)
window = win32gui.FindWindow(None, WINDOW_TITLE)
win32gui.SetForegroundWindow(window)
pos = win32gui.GetWindowRect(window)
print("Window found at:" + str(pos))
return pos[0], pos[1]
def getScreenImage():
print('capturing screenshot...')
scim = ImageGrab.grab()
scim.save('screen.png')
return cv2.imread("screen.png")
def getAllSquare(screen_image, game_pos):
print('cutting pics...')
game_x = game_pos[0] + MARGIN_LEFT
game_y = game_pos[1] + MARGIN_HEIGHT
all_square = []
print("width:",len(screen_image))
print("height:",len(screen_image[0]))
for x in range(0, COL_NUM):
for y in range(0, ROW_NUM):
square = screen_image[game_y + y*SQUARE_HEIGHT:
game_y + (y+1)*SQUARE_HEIGHT,
game_x + x*SQUARE_WIDTH:
game_x + (x+1)*SQUARE_WIDTH]
all_square.append(square)
return list(map(lambda square:
square[SUB_LT_Y:SUB_RB_Y, SUB_LT_X:SUB_RB_X], all_square))
def subImageSquare(img1,img2):
res = 0
for i in range(len(img1)):
for j in range(len(img1[0])):
res += abs(int(img1[i][j][0]) - int(img2[i][j][0])) + \
abs(int(img1[i][j][1]) - int(img2[i][j][1])) + \
abs(int(img1[i][j][2]) - int(img2[i][j][2]))
return res
def getAllImageTypes(all_square):
print("sorting pics...")
empty_img = cv2.imread('empty.png')
types = set([0])
for square in all_square:
types.add(subImageSquare(empty_img,square))
return sorted(list(types))
def generateMatrix(all_square,imgTypes):
mat = []
empty_img = cv2.imread('empty.png')
for square in all_square:
diff = subImageSquare(empty_img,square)
for i in range(len(imgTypes)):
if diff == imgTypes[i]:
mat.append(i)
return mat
def checkConnect(x,y,mat):
width = COL_NUM
height = ROW_NUM
if x[0] < 0 or x[1] < 0 or x[0] >= width or x[1] >= height or\
y[0] < 0 or y[1] < 0 or y[0] >= width or y[1] >= width:
return False
if x[0] != y[0] and x[1] != y[1]:
return False
#check row
if x[0] == y[0]:
for i in range(min(x[1],y[1])+1,max(x[1],y[1])):
if mat[x[0]*height+i] > 0:
return False
#check colum
if x[1] == y[1]:
for i in range(min(x[0],y[0])+1,max(x[0],y[0])):
if mat[i*height+x[1]] > 0:
return False
return True
def isCanLink(x,y,mat):
width = COL_NUM
height = ROW_NUM
#check same value
if mat[x[0]*height+x[1]] != mat[y[0]*height+y[1]]:
return False
#one step check
if checkConnect(x,y,mat):
return True
#two step check
if mat[x[0]*height+y[1]] == 0:
if checkConnect(x,(x[0],y[1]),mat) and \
checkConnect((x[0],y[1]),y,mat):
return True
if mat[y[0]*height+x[1]] == 0:
if checkConnect(x,(y[0],x[1]),mat) and \
checkConnect((y[0],x[1]),y,mat):
return True
#three step check
for i in range(0,height):
if mat[x[0]*height+i] == 0 and mat[y[0]*height+i] == 0:
if checkConnect(x,(x[0],i),mat) and \
checkConnect((y[0],i),y,mat) and \
checkConnect((x[0],i),(y[0],i),mat):
return True
for i in range(0,width):
if mat[i*height+x[1]] == 0 and mat[i*height+y[1]] == 0:
if checkConnect(x,(i,x[1]),mat) and \
checkConnect((i,y[1]),y,mat) and \
checkConnect((i,x[1]),(i,y[1]),mat):
return True
def autoMouseClick(pos,delay = 0.001):
win32api.SetCursorPos((pos[0],pos[1]))
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,pos[0],pos[1], 0, 0)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,pos[0],pos[1],0,0)
time.sleep(TIME_INTERVAL())
def autoRemove(mat,game_pos):
game_x = game_pos[0] + MARGIN_LEFT
game_y = game_pos[1] + MARGIN_HEIGHT
width = COL_NUM
height = ROW_NUM
remove = 0
for i in range(width):
for j in range(height):
if mat[i*height+j] > 0:
remove += 1
while remove > 0:
for i in range(len(mat)):
for j in range(len(mat)):
if i != j and mat[i] == mat[j] and mat[i] > 0:
px = (i//height,i%height)
py = (j//height,j%height)
if isCanLink(px,py,mat):
x1 = game_x + px[0]*SQUARE_WIDTH
y1 = game_y + px[1]*SQUARE_HEIGHT
x2 = game_x + py[0]*SQUARE_WIDTH
y2 = game_y + py[1]*SQUARE_HEIGHT
pos_x = (x1+CLICK_POS_X,y1+CLICK_POS_Y)
pos_y = (x2+CLICK_POS_X,y2+CLICK_POS_Y)
autoMouseClick(pos_x)
autoMouseClick(pos_y)
mat[i] = 0
mat[j] = 0
remove -= 2
print(px,py)
print("remove one pair:",pos_x,pos_y)
if __name__ == '__main__':
game_pos = getGameWindowPosition()
time.sleep(1)
screen_image = getScreenImage()
all_square_list = getAllSquare(screen_image, game_pos)
imgTypes = getAllImageTypes(all_square_list)
mat = generateMatrix(all_square_list,imgTypes)
autoRemove(mat,game_pos)