Python怎么实现自动玩连连看
发表于:2024-11-17 作者:千家信息网编辑
千家信息网最后更新 2024年11月17日,这篇"Python怎么实现自动玩连连看"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇"
千家信息网最后更新 2024年11月17日Python怎么实现自动玩连连看
这篇"Python怎么实现自动玩连连看"文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇"Python怎么实现自动玩连连看"文章吧。
实现步骤
模块导入
import cv2import numpy as npimport win32apiimport win32guiimport win32confrom PIL import ImageGrabimport timeimport random
窗体标题 用于定位游戏窗体
WINDOW_TITLE = "连连看"
时间间隔随机生成 [MIN,MAX]
TIME_INTERVAL_MAX = 0.06TIME_INTERVAL_MIN = 0.1
游戏区域距离顶点的x偏移
MARGIN_LEFT = 10
游戏区域距离顶点的y偏移
MARGIN_HEIGHT = 180
横向的方块数量
H_NUM = 19
纵向的方块数量
V_NUM = 11
方块宽度
POINT_WIDTH = 31
方块高度
POINT_HEIGHT = 35
空图像编号
EMPTY_ID = 0
切片处理时候的左上、右下坐标:
SUB_LT_X = 8SUB_LT_Y = 8SUB_RB_X = 27SUB_RB_Y = 27
游戏的最多消除次数
MAX_ROUND = 200
获取窗体坐标位置
def getGameWindow(): # FindWindow(lpClassName=None, lpWindowName=None) 窗口类名 窗口标题名 window = win32gui.FindWindow(None, WINDOW_TITLE) # 没有定位到游戏窗体 while not window: print('Failed to locate the game window , reposition the game window after 10 seconds...') time.sleep(10) window = win32gui.FindWindow(None, WINDOW_TITLE) # 定位到游戏窗体 # 置顶游戏窗口 win32gui.SetForegroundWindow(window) pos = win32gui.GetWindowRect(window) print("Game windows at " + str(pos)) return (pos[0], pos[1])
获取屏幕截图
def getScreenImage(): print('Shot screen...') # 获取屏幕截图 Image类型对象 scim = ImageGrab.grab() scim.save('screen.png') # 用opencv读取屏幕截图 # 获取ndarray return cv2.imread("screen.png")
从截图中分辨图片 处理成地图
def getAllSquare(screen_image, game_pos): print('Processing pictures...') # 通过游戏窗体定位 # 加上偏移量获取游戏区域 game_x = game_pos[0] + MARGIN_LEFT game_y = game_pos[1] + MARGIN_HEIGHT # 从游戏区域左上开始 # 把图像按照具体大小切割成相同的小块 # 切割标准是按照小块的横纵坐标 all_square = [] for x in range(0, H_NUM): for y in range(0, V_NUM): # ndarray的切片方法 : [纵坐标起始位置:纵坐标结束为止,横坐标起始位置:横坐标结束位置] square = screen_image[game_y + y * POINT_HEIGHT:game_y + (y + 1) * POINT_HEIGHT, game_x + x * POINT_WIDTH:game_x + (x + 1) * POINT_WIDTH] all_square.append(square) # 因为有些图片的边缘会造成干扰,所以统一把图片往内缩小一圈 # 对所有的方块进行处理 ,去掉边缘一圈后返回 finalresult = [] for square in all_square: s = square[SUB_LT_Y:SUB_RB_Y, SUB_LT_X:SUB_RB_X] finalresult.append(s) return finalresult
判断列表中是否存在相同图形
存在返回进行判断图片所在的id
否则返回-1
def isImageExist(img, img_list): i = 0 for existed_img in img_list: # 两个图片进行比较 返回的是两个图片的标准差 b = np.subtract(existed_img, img) # 若标准差全为0 即两张图片没有区别 if not np.any(b): return i i = i + 1 return -1
获取所有的方块类型
def getAllSquareTypes(all_square): print("Init pictures types...") types = [] # number列表用来记录每个id的出现次数 number = [] # 当前出现次数最多的方块 # 这里我们默认出现最多的方块应该是空白块 nowid = 0; for square in all_square: nid = isImageExist(square, types) # 如果这个图像不存在则插入列表 if nid == -1: types.append(square) number.append(1); else: # 若这个图像存在则给计数器 + 1 number[nid] = number[nid] + 1 if (number[nid] > number[nowid]): nowid = nid # 更新EMPTY_ID # 即判断在当前这张图中的空白块id global EMPTY_ID EMPTY_ID = nowid print('EMPTY_ID = ' + str(EMPTY_ID)) return types
将二维图片矩阵转换为二维数字矩阵
注意因为在上面对截屏切片时是以列为优先切片的
所以生成的record二维矩阵每行存放的其实是游戏屏幕中每列的编号
换个说法就是record其实是游戏屏幕中心对称后的列表
def getAllSquareRecord(all_square_list, types): print("Change map...") record = [] line = [] for square in all_square_list: num = 0 for type in types: res = cv2.subtract(square, type) if not np.any(res): line.append(num) break num += 1 # 每列的数量为V_NUM # 那么当当前的line列表中存在V_NUM个方块时我们认为本列处理完毕 if len(line) == V_NUM: print(line); record.append(line) line = [] return record
判断给出的两个图像能否消除
def canConnect(x1, y1, x2, y2, r): result = r[:] # 如果两个图像中有一个为0 直接返回False if result[x1][y1] == EMPTY_ID or result[x2][y2] == EMPTY_ID: return False if x1 == x2 and y1 == y2: return False if result[x1][y1] != result[x2][y2]: return False # 判断横向连通 if horizontalCheck(x1, y1, x2, y2, result): return True # 判断纵向连通 if verticalCheck(x1, y1, x2, y2, result): return True # 判断一个拐点可连通 if turnOnceCheck(x1, y1, x2, y2, result): return True # 判断两个拐点可连通 if turnTwiceCheck(x1, y1, x2, y2, result): return True # 不可联通返回False return False
判断横向联通
def horizontalCheck(x1, y1, x2, y2, result): if x1 == x2 and y1 == y2: return False if x1 != x2: return False startY = min(y1, y2) endY = max(y1, y2) # 判断两个方块是否相邻 if (endY - startY) == 1: return True # 判断两个方块通路上是否都是0,有一个不是,就说明不能联通,返回false for i in range(startY + 1, endY): if result[x1][i] != EMPTY_ID: return False return True
判断纵向联通
def verticalCheck(x1, y1, x2, y2, result): if x1 == x2 and y1 == y2: return False if y1 != y2: return False startX = min(x1, x2) endX = max(x1, x2) # 判断两个方块是否相邻 if (endX - startX) == 1: return True # 判断两方块儿通路上是否可连。 for i in range(startX + 1, endX): if result[i][y1] != EMPTY_ID: return False return True
判断一个拐点可联通
def turnOnceCheck(x1, y1, x2, y2, result): if x1 == x2 or y1 == y2: return False cx = x1 cy = y2 dx = x2 dy = y1 # 拐点为空,从第一个点到拐点并且从拐点到第二个点可通,则整条路可通。 if result[cx][cy] == EMPTY_ID: if horizontalCheck(x1, y1, cx, cy, result) and verticalCheck(cx, cy, x2, y2, result): return True if result[dx][dy] == EMPTY_ID: if verticalCheck(x1, y1, dx, dy, result) and horizontalCheck(dx, dy, x2, y2, result): return True return False
判断两个拐点可联通
def turnTwiceCheck(x1, y1, x2, y2, result): if x1 == x2 and y1 == y2: return False # 遍历整个数组找合适的拐点 for i in range(0, len(result)): for j in range(0, len(result[1])): # 不为空不能作为拐点 if result[i][j] != EMPTY_ID: continue # 不和被选方块在同一行列的不能作为拐点 if i != x1 and i != x2 and j != y1 and j != y2: continue # 作为交点的方块不能作为拐点 if (i == x1 and j == y2) or (i == x2 and j == y1): continue if turnOnceCheck(x1, y1, i, j, result) and ( horizontalCheck(i, j, x2, y2, result) or verticalCheck(i, j, x2, y2, result)): return True if turnOnceCheck(i, j, x2, y2, result) and ( horizontalCheck(x1, y1, i, j, result) or verticalCheck(x1, y1, i, j, result)): return True return False
自动消除
def autoRelease(result, game_x, game_y): # 遍历地图 for i in range(0, len(result)): for j in range(0, len(result[0])): # 当前位置非空 if result[i][j] != EMPTY_ID: # 再次遍历地图 寻找另一个满足条件的图片 for m in range(0, len(result)): for n in range(0, len(result[0])): if result[m][n] != EMPTY_ID: # 若可以执行消除 if canConnect(i, j, m, n, result): # 消除的两个位置设置为空 result[i][j] = EMPTY_ID result[m][n] = EMPTY_ID print('Remove :' + str(i + 1) + ',' + str(j + 1) + ' and ' + str(m + 1) + ',' + str( n + 1)) # 计算当前两个位置的图片在游戏中应该存在的位置 x1 = game_x + j * POINT_WIDTH y1 = game_y + i * POINT_HEIGHT x2 = game_x + n * POINT_WIDTH y2 = game_y + m * POINT_HEIGHT # 模拟鼠标点击第一个图片所在的位置 win32api.SetCursorPos((x1 + 15, y1 + 18)) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x1 + 15, y1 + 18, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x1 + 15, y1 + 18, 0, 0) # 等待随机时间 ,防止检测 time.sleep(random.uniform(TIME_INTERVAL_MIN, TIME_INTERVAL_MAX)) # 模拟鼠标点击第二个图片所在的位置 win32api.SetCursorPos((x2 + 15, y2 + 18)) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x2 + 15, y2 + 18, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x2 + 15, y2 + 18, 0, 0) time.sleep(random.uniform(TIME_INTERVAL_MIN, TIME_INTERVAL_MAX)) # 执行消除后返回True return True return False
效果的话得上传视频,截图展现不出来效果,大家可以自行试试。
全部代码
# -*- coding:utf-8 -*-import cv2import numpy as npimport win32apiimport win32guiimport win32confrom PIL import ImageGrabimport timeimport random# 窗体标题 用于定位游戏窗体WINDOW_TITLE = "连连看"# 时间间隔随机生成 [MIN,MAX]TIME_INTERVAL_MAX = 0.06TIME_INTERVAL_MIN = 0.1# 游戏区域距离顶点的x偏移MARGIN_LEFT = 10# 游戏区域距离顶点的y偏移MARGIN_HEIGHT = 180# 横向的方块数量H_NUM = 19# 纵向的方块数量V_NUM = 11# 方块宽度POINT_WIDTH = 31# 方块高度POINT_HEIGHT = 35# 空图像编号EMPTY_ID = 0# 切片处理时候的左上、右下坐标:SUB_LT_X = 8SUB_LT_Y = 8SUB_RB_X = 27SUB_RB_Y = 27# 游戏的最多消除次数MAX_ROUND = 200def getGameWindow(): # FindWindow(lpClassName=None, lpWindowName=None) 窗口类名 窗口标题名 window = win32gui.FindWindow(None, WINDOW_TITLE) # 没有定位到游戏窗体 while not window: print('Failed to locate the game window , reposition the game window after 10 seconds...') time.sleep(10) window = win32gui.FindWindow(None, WINDOW_TITLE) # 定位到游戏窗体 # 置顶游戏窗口 win32gui.SetForegroundWindow(window) pos = win32gui.GetWindowRect(window) print("Game windows at " + str(pos)) return (pos[0], pos[1])def getScreenImage(): print('Shot screen...') # 获取屏幕截图 Image类型对象 scim = ImageGrab.grab() scim.save('screen.png') # 用opencv读取屏幕截图 # 获取ndarray return cv2.imread("screen.png")def getAllSquare(screen_image, game_pos): print('Processing pictures...') # 通过游戏窗体定位 # 加上偏移量获取游戏区域 game_x = game_pos[0] + MARGIN_LEFT game_y = game_pos[1] + MARGIN_HEIGHT # 从游戏区域左上开始 # 把图像按照具体大小切割成相同的小块 # 切割标准是按照小块的横纵坐标 all_square = [] for x in range(0, H_NUM): for y in range(0, V_NUM): # ndarray的切片方法 : [纵坐标起始位置:纵坐标结束为止,横坐标起始位置:横坐标结束位置] square = screen_image[game_y + y * POINT_HEIGHT:game_y + (y + 1) * POINT_HEIGHT, game_x + x * POINT_WIDTH:game_x + (x + 1) * POINT_WIDTH] all_square.append(square) # 因为有些图片的边缘会造成干扰,所以统一把图片往内缩小一圈 # 对所有的方块进行处理 ,去掉边缘一圈后返回 finalresult = [] for square in all_square: s = square[SUB_LT_Y:SUB_RB_Y, SUB_LT_X:SUB_RB_X] finalresult.append(s) return finalresult# 判断列表中是否存在相同图形# 存在返回进行判断图片所在的id# 否则返回-1def isImageExist(img, img_list): i = 0 for existed_img in img_list: # 两个图片进行比较 返回的是两个图片的标准差 b = np.subtract(existed_img, img) # 若标准差全为0 即两张图片没有区别 if not np.any(b): return i i = i + 1 return -1def getAllSquareTypes(all_square): print("Init pictures types...") types = [] # number列表用来记录每个id的出现次数 number = [] # 当前出现次数最多的方块 # 这里我们默认出现最多的方块应该是空白块 nowid = 0; for square in all_square: nid = isImageExist(square, types) # 如果这个图像不存在则插入列表 if nid == -1: types.append(square) number.append(1); else: # 若这个图像存在则给计数器 + 1 number[nid] = number[nid] + 1 if (number[nid] > number[nowid]): nowid = nid # 更新EMPTY_ID # 即判断在当前这张图中的空白块id global EMPTY_ID EMPTY_ID = nowid print('EMPTY_ID = ' + str(EMPTY_ID)) return types# 将二维图片矩阵转换为二维数字矩阵# 注意因为在上面对截屏切片时是以列为优先切片的# 所以生成的record二维矩阵每行存放的其实是游戏屏幕中每列的编号# 换个说法就是record其实是游戏屏幕中心对称后的列表def getAllSquareRecord(all_square_list, types): print("Change map...") record = [] line = [] for square in all_square_list: num = 0 for type in types: res = cv2.subtract(square, type) if not np.any(res): line.append(num) break num += 1 # 每列的数量为V_NUM # 那么当当前的line列表中存在V_NUM个方块时我们认为本列处理完毕 if len(line) == V_NUM: print(line); record.append(line) line = [] return recorddef canConnect(x1, y1, x2, y2, r): result = r[:] # 如果两个图像中有一个为0 直接返回False if result[x1][y1] == EMPTY_ID or result[x2][y2] == EMPTY_ID: return False if x1 == x2 and y1 == y2: return False if result[x1][y1] != result[x2][y2]: return False # 判断横向连通 if horizontalCheck(x1, y1, x2, y2, result): return True # 判断纵向连通 if verticalCheck(x1, y1, x2, y2, result): return True # 判断一个拐点可连通 if turnOnceCheck(x1, y1, x2, y2, result): return True # 判断两个拐点可连通 if turnTwiceCheck(x1, y1, x2, y2, result): return True # 不可联通返回False return Falsedef horizontalCheck(x1, y1, x2, y2, result): if x1 == x2 and y1 == y2: return False if x1 != x2: return False startY = min(y1, y2) endY = max(y1, y2) # 判断两个方块是否相邻 if (endY - startY) == 1: return True # 判断两个方块通路上是否都是0,有一个不是,就说明不能联通,返回false for i in range(startY + 1, endY): if result[x1][i] != EMPTY_ID: return False return Truedef verticalCheck(x1, y1, x2, y2, result): if x1 == x2 and y1 == y2: return False if y1 != y2: return False startX = min(x1, x2) endX = max(x1, x2) # 判断两个方块是否相邻 if (endX - startX) == 1: return True # 判断两方块儿通路上是否可连。 for i in range(startX + 1, endX): if result[i][y1] != EMPTY_ID: return False return Truedef turnOnceCheck(x1, y1, x2, y2, result): if x1 == x2 or y1 == y2: return False cx = x1 cy = y2 dx = x2 dy = y1 # 拐点为空,从第一个点到拐点并且从拐点到第二个点可通,则整条路可通。 if result[cx][cy] == EMPTY_ID: if horizontalCheck(x1, y1, cx, cy, result) and verticalCheck(cx, cy, x2, y2, result): return True if result[dx][dy] == EMPTY_ID: if verticalCheck(x1, y1, dx, dy, result) and horizontalCheck(dx, dy, x2, y2, result): return True return Falsedef turnTwiceCheck(x1, y1, x2, y2, result): if x1 == x2 and y1 == y2: return False # 遍历整个数组找合适的拐点 for i in range(0, len(result)): for j in range(0, len(result[1])): # 不为空不能作为拐点 if result[i][j] != EMPTY_ID: continue # 不和被选方块在同一行列的不能作为拐点 if i != x1 and i != x2 and j != y1 and j != y2: continue # 作为交点的方块不能作为拐点 if (i == x1 and j == y2) or (i == x2 and j == y1): continue if turnOnceCheck(x1, y1, i, j, result) and ( horizontalCheck(i, j, x2, y2, result) or verticalCheck(i, j, x2, y2, result)): return True if turnOnceCheck(i, j, x2, y2, result) and ( horizontalCheck(x1, y1, i, j, result) or verticalCheck(x1, y1, i, j, result)): return True return Falsedef autoRelease(result, game_x, game_y): # 遍历地图 for i in range(0, len(result)): for j in range(0, len(result[0])): # 当前位置非空 if result[i][j] != EMPTY_ID: # 再次遍历地图 寻找另一个满足条件的图片 for m in range(0, len(result)): for n in range(0, len(result[0])): if result[m][n] != EMPTY_ID: # 若可以执行消除 if canConnect(i, j, m, n, result): # 消除的两个位置设置为空 result[i][j] = EMPTY_ID result[m][n] = EMPTY_ID print('Remove :' + str(i + 1) + ',' + str(j + 1) + ' and ' + str(m + 1) + ',' + str( n + 1)) # 计算当前两个位置的图片在游戏中应该存在的位置 x1 = game_x + j * POINT_WIDTH y1 = game_y + i * POINT_HEIGHT x2 = game_x + n * POINT_WIDTH y2 = game_y + m * POINT_HEIGHT # 模拟鼠标点击第一个图片所在的位置 win32api.SetCursorPos((x1 + 15, y1 + 18)) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x1 + 15, y1 + 18, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x1 + 15, y1 + 18, 0, 0) # 等待随机时间 ,防止检测 time.sleep(random.uniform(TIME_INTERVAL_MIN, TIME_INTERVAL_MAX)) # 模拟鼠标点击第二个图片所在的位置 win32api.SetCursorPos((x2 + 15, y2 + 18)) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x2 + 15, y2 + 18, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x2 + 15, y2 + 18, 0, 0) time.sleep(random.uniform(TIME_INTERVAL_MIN, TIME_INTERVAL_MAX)) # 执行消除后返回True return True return Falsedef autoRemove(squares, game_pos): game_x = game_pos[0] + MARGIN_LEFT game_y = game_pos[1] + MARGIN_HEIGHT # 重复一次消除直到到达最多消除次数 while True: if not autoRelease(squares, game_x, game_y): # 当不再有可消除的方块时结束 , 返回消除数量 returnif __name__ == '__main__': random.seed() # i. 定位游戏窗体 game_pos = getGameWindow() time.sleep(1) # ii. 获取屏幕截图 screen_image = getScreenImage() # iii. 对截图切片,形成一张二维地图 all_square_list = getAllSquare(screen_image, game_pos) # iv. 获取所有类型的图形,并编号 types = getAllSquareTypes(all_square_list) # v. 讲获取的图片地图转换成数字矩阵 result = np.transpose(getAllSquareRecord(all_square_list, types)) # vi. 执行消除 , 并输出消除数量 print('The total elimination amount is ' + str(autoRemove(result, game_pos)))
以上就是关于"Python怎么实现自动玩连连看"这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注行业资讯频道。
方块
图片
两个
拐点
位置
窗体
图像
屏幕
截图
定位
区域
数量
地图
次数
矩阵
二维
处理
所在
标准
纵坐标
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
健康小屋网络技术
失落方舟俄服选择哪个服务器
沧州市公安局网络安全大队
怎么设置数据库默认格式
系统涉密软件开发资质
制造软件开发需要学什么
方舟手游ark怎么进服务器
声誉好的语音聊天软件开发
热拉找不到服务器
it软件开发是什么
苹果macos数据库
电脑如何搜索网络打印机服务器
中邮揽投软件开发服务电话
网络技术学什么好
云服务器还需要浏览器吗
超全栈软件开发
关于成长网络安全小卫士
计算机网络技术考试有几级
文旅局网络安全宣传总结
互联网与科技融合发展
江西名微汇网络技术有限公司
宜通世纪网络安全概念
健康小屋网络技术
鲜行互联网科技有限公司
千锋教育网络安全工程师教程
特别推荐网络安全知识小课堂
凡科互联网科技有哪些产品
网络安全法61起施行
linux搭建dns服务器
服务器里面哪个文件是启动文件