#-*- coding:utf-8 -*-
from win32 importwin32guiimporttimefrom PIL importImageGrab, Imageimportnumpy as npimportoperatorfrom pymouse importPyMouseimportwin32guiclassGameAssist:def __init__(self, wdname):"""初始化"""
#取得窗口句柄
self.hwnd =win32gui.FindWindow(0, wdname)if notself.hwnd:print("窗口找不到,请确认窗口句柄名称:【%s】" %wdname )
exit()#窗口显示最前面
win32gui.SetForegroundWindow(self.hwnd)#小图标编号矩阵
self.im2num_arr =[]#主截图的左上角坐标和右下角坐标
self.scree_left_and_right_point = (577, 289, 1043, 599)#小图标宽高
self.im_width = 39
#PyMouse对象,鼠标点击
self.mouse =PyMouse()defscreenshot(self):"""屏幕截图"""
#1、用grab函数截图,参数为左上角和右下角左标
#image = ImageGrab.grab((417, 257, 885, 569))
image =ImageGrab.grab(self.scree_left_and_right_point)#2、分切小图
#exit()
image_list ={}
offset= self.im_width #39
#8行12列
for x in range(8):
image_list[x]={}for y in range(12):#print("show",x, y)
#exit()
top = x *offset
left= y *offset
right= (y + 1) *offset
bottom= (x + 1) *offset#用crop函数切割成小图标,参数为图标的左上角和右下角左边
im =image.crop((left, top, right, bottom))#将切割好的图标存入对应的位置
image_list[x][y] =imreturnimage_listdefimage2num(self, image_list):"""将图标矩阵转换成数字矩阵"""
#1、创建全零矩阵和空的一维数组
arr = np.zeros((10, 14), dtype=np.int32) #以数字代替图片
image_type_list =[]#2、识别出不同的图片,将图片矩阵转换成数字矩阵
for i inrange(len(image_list)):for j inrange(len(image_list[0])):
im=image_list[i][j]#验证当前图标是否已存入
index =self.getIndex(im, image_type_list)#不存在image_type_list
if index <0:
image_type_list.append(im)
arr[i+ 1][j + 1] =len(image_type_list)else:
arr[i+ 1][j + 1] = index + 1
print("图标数:", len(image_type_list))
self.im2num_arr=arrreturnarr#检查数组中是否有图标,如果有则返回索引下表
defgetIndex(self,im, im_list):for i inrange(len(im_list)):ifself.isMatch(im, im_list[i]):returnireturn -1
#汉明距离判断两个图标是否一样
defisMatch(self, im1, im2):#缩小图标,转成灰度
image1 = im1.resize((20, 20), Image.ANTIALIAS).convert("L")
image2= im2.resize((20, 20), Image.ANTIALIAS).convert("L")#将灰度图标转成01串,即系二进制数据
pixels1 =list(image1.getdata())
pixels2=list(image2.getdata())
avg1= sum(pixels1) /len(pixels1)
avg2= sum(pixels2) /len(pixels2)
hash1= "".join(map(lambda p: "1" if p > avg1 else "0", pixels1))
hash2= "".join(map(lambda p: "1" if p > avg2 else "0", pixels2))#统计两个01串不同数字的个数
match =sum(map(operator.ne, hash1, hash2))#原作者郭靖愕然阀值设为10,实测400位数字,50合适
return match < 50
#判断矩阵是否全为0
defisAllZero(self, arr):for i in range(1, 9):for j in range(1, 13):if arr[i][j] !=0:returnFalsereturnTrue#是否为同行或同列且可连
defisReachable(self, x1, y1, x2, y2):#1、先判断值是否相同
if self.im2num_arr[x1][y1] !=self.im2num_arr[x2][y2]:returnFalse#1、分别获取两个坐标同行或同列可连的坐标数组
list1 =self.getDirectConnectList(x1, y1)
list2=self.getDirectConnectList(x2, y2)#print(x1, y1, list1)
#print(x2, y2, list2)
#exit()
#2、比较坐标数组中是否可连
for x1, y1 inlist1:for x2, y2 inlist2:ifself.isDirectConnect(x1, y1, x2, y2):returnTruereturnFalse#获取同行或同列可连的坐标数组
defgetDirectConnectList(self, x, y):
plist=[]for px in range(0, 10):for py in range(0, 14):#获取同行或同列且为0的坐标
if self.im2num_arr[px][py] == 0 andself.isDirectConnect(x, y, px, py):
plist.append([px, py])returnplist#是否为同行或同列且可连
defisDirectConnect(self, x1, y1, x2, y2):#1、位置完全相同
if x1 == x2 and y1 ==y2:returnFalse#2、行列都不同的
if x1 != x2 and y1 !=y2:returnFalse#3、同行
if x1 == x2 andself.isRowConnect(x1, y1, y2):returnTrue#4、同列
if y1 == y2 andself.isColConnect(y1, x1, x2):returnTruereturnFalse#判断同行是否可连
defisRowConnect(self, x, y1, y2):
minY=min(y1, y2)
maxY=max(y1, y2)#相邻直接可连
if maxY - minY == 1:returnTrue#判断两个坐标之间是否全为0
for y0 in range(minY + 1, maxY):if self.im2num_arr[x][y0] !=0:returnFalsereturnTrue#判断同列是否可连
defisColConnect(self, y, x1, x2):
minX=min(x1, x2)
maxX=max(x1, x2)#相邻直接可连
if maxX - minX == 1:returnTrue#判断两个坐标之间是否全为0
for x0 in range(minX + 1, maxX):if self.im2num_arr[x0][y] !=0:returnFalsereturnTrue#点击事件并设置数组为0
defclickAndSetZero(self, x1, y1, x2, y2):#print("click", x1, y1, x2, y2)
#(299, 251, 768, 564)
#原理:左上角图标中点 + 偏移量
p1_x = int(self.scree_left_and_right_point[0] + (y1 - 1)*self.im_width + (self.im_width / 2))
p1_y= int(self.scree_left_and_right_point[1] + (x1 - 1)*self.im_width + (self.im_width / 2))
p2_x= int(self.scree_left_and_right_point[0] + (y2 - 1)*self.im_width + (self.im_width / 2))
p2_y= int(self.scree_left_and_right_point[1] + (x2 - 1)*self.im_width + (self.im_width / 2))
time.sleep(0.1)#原作者郭靖愕然用的click方法,实测click为双击,press单击
self.mouse.press(p1_x, p1_y)
time.sleep(0.1)
self.mouse.press(p2_x, p2_y)#设置矩阵值为0
#while self.im2num_arr[x1][y1-1]!=0
#上面都是0没有图标,就置矩阵值为0
self.im2num_arr[x1][y1]=0
self.im2num_arr[x2][y2]=0print("消除:(%d, %d) (%d, %d)" %(x1, y1, x2, y2))#exit()
#程序入口、控制中心
defstart(self):#1、先截取游戏区域大图,然后分切每个小图
image_list =self.screenshot()#2、识别小图标,收集编号
self.image2num(image_list)print(self.im2num_arr)#3、遍历查找可以相连的坐标
while notself.isAllZero(self.im2num_arr):for x1 in range(1, 9):for y1 in range(1, 13):if self.im2num_arr[x1][y1] ==0:continue
for x2 in range(1, 9):for y2 in range(1, 13):#跳过为0 或者同一个
if self.im2num_arr[x2][y2] == 0 or (x1 == x2 and y1 ==y2):continue
ifself.isReachable(x1, y1, x2, y2):
self.clickAndSetZero(x1, y1, x2, y2)if __name__ == "__main__":#wdname 为连连看窗口的名称,必须写完整
wdname = '宠物连连看经典版2,宠物连连看经典版2小游戏,4399小游戏 www.4399.com - Google Chrome'demo=GameAssist(wdname)
demo.start()