引言

期末的时候看到一篇博客,写的宠物连连看的辅助脚本,感觉很有意思,就自己跟着博客自己实现了一遍,开发过程中遇到了一些问题,也体会到了解决问题的乐趣,遂在此记录一下。

先放一下博客的链接:https://www.cnblogs.com/reader/p/10111777.html

这篇博客给出了完整版的代码,大体上我是根据他的思路实现的,只有部分细节按照我自己的想法做了修改。

一、总体开发思路

1 游戏规则

  • 鼠标操作

    • 鼠标点击两个相同图标,如果图标间的连线转折不超过两次并且不经过其他图标,即可消除。
  • 游戏目标
    • 在规定的时间内消去所有的牌。

2 实现流程

​ 具体流程如下:

  • 首先获取屏幕截图,然后将截图分割成一个个小图标,可以得到一个图标矩阵(8*12)
  • 然后将每种图标转化成数字,得到一个数字矩阵,相同的数字表示相同的图标
  • 核心算法是根据游戏规则找到可以相连的两个图标然后模拟鼠标点击消去

二、 具体实现

1、获取窗口句柄、并将窗口显示在最前面

窗口句柄: 简单理解就是窗口的id,可以根据这个id识别已经打开的窗口

在Windows中,句柄是一个系统内部数据结构的引用。例如当你操作一个窗口,或说是一个Delphi窗体时,系统会给你一个该窗口的句柄,系统会通知你:你正在操作142号窗口,就此你的应用程序就能要求系统对142号窗口进行操作——移动窗口、改变窗口大小、把窗口最小化等等。

–《百度百科》

可以使用winspy,spy++ 等工具获取某个窗口的句柄。

使用360浏览器打开宠物连连看小游戏后,我用winspy获取到的句柄是"宠物连连看经典版2小游戏,在线玩,4399小游戏 - 360安全浏览器 10.0"

获取到窗口的句柄之后,就可以使用python的库操纵窗口了,使用的库是win32gui

# 获取窗口的句柄
self.hwnd = win32gui.FindWindow(0, wdname)
if self.hwnd == 0:print('no such hwnd')exit(1)
# 将该窗口显示在最前面
win32gui.SetForegroundWindow(self.hwnd)

执行之后,连连看的窗口就显示到最前面了,下一步就可以截图了

2、 获取屏幕截图,并将图像分割,得到一个图标矩阵

主要使用到的库是PIL的ImageGrab,PIL现在官网上不去,pillow好像和PIL功能是差不多的,文档可以参考pillow的。

这一步的重点是要计算出每个图标的左上角、右下角的坐标,准确将其从截图中分割出来。

     '''获取屏幕截图,并将图标分割'''def screen_grab(self):# 获取整个屏幕截图image_grab = ImageGrab.grab()# 获取截图中间所有动物图标的截图box = (399, 305, 1247, 873)animals_iamge  = image_grab.crop(box)# 将每个动物图像分割,得到图像矩阵images_list = []offset = 71   # 将截图用windows自带的画图打开,就可以查看某个点的位置,# 计算出每个图标的大小大约是71px,不是特别准确,但是基本可以分割出每个图标了x0 = 0y0 = 0for i in range(8):images_row = []for j in range(12):# 小图标左上角的坐标x1 = x0 + j*offsety1 = y0 + i*offset# 小图标右下角的坐标x2 = x1 + offsety2 = y1 + offset# 5px的偏移是为了去掉小图标周围,只保留中间,这样区分不同的图片更容易images_row.append(animals_iamge.crop((x1+5, y1+5, x2-5, y2-5)))images_list.append(images_row)return images_list

3、将图标矩阵转换成数字矩阵

这一步是比较复杂的,主要的目标是将每个图标转换成一个数字,要求相同的图标数字相同。

这里分为两步:

  • 首先将每个图标转成一个灰度图标,然后将这个灰度图标转换成01字符串。

  • 然后比较两个字符串各个位置0、1的区别,记录不同的个数,然后设定一个阈值,不同的个数如果低于这个阈值(即两个图标相差不多),可以认为它们是同一种图标,否者不是。

比较麻烦的是阈值的确定,只能将两个图片的灰度值慢慢比较,找到一个threshold,高于这个threshold能区分为两个不同的图片;低于这threshold保证两个图标相同。

我用到的一个技巧就是,在创建图标矩阵的时候(上一步),每个图标截取的时候往中间多收缩了5px,这样就可以去掉截取的图标周围的一些"杂质",更容易确定阈值。

    def get_index(self, str01, threshold, str01_list):for i in range(len(str01_list)):diff = sum(map(operator.ne, str01, str01_list[i]))if diff < threshold:return ireturn -1    '''将每个图片转换成一个数字,相同的图标数字相同'''def image2num(self, animal_images):# 将每个图标转换成灰度图标,然后再将灰度图标转换成一个01字符串num_str01_matrix = []for i in range(8):num_row = []for j in range(12):im = animal_images[i][j]im_L = im.convert("L")# im_L.show()pixels = list(im_L.getdata())  # 每个点的像素值avg_pixel = sum(pixels) / len(pixels)str01 = "".join(map(lambda x: "1" if x > avg_pixel else "0", pixels))num_row.append(str01)num_str01_matrix.append(num_row)# 将每个01字符串转换成一个数字threshold = 800 # 低于这个阈值认为是同一种图片image_type_list = [] # 所有图标的类型num_matrix = np.zeros((8,12), dtype=np.uint32)  #创建一个全0矩阵for i in range(8):for j in range(12):index = self.get_index(num_str01_matrix[i][j], threshold, image_type_list)if index < 0:image_type_list.append(num_str01_matrix[i][j])num_matrix[i][j] = len(image_type_list)else:num_matrix[i][j] = index+1return num_matrix

得到的一个矩阵实例

[[ 0  0  0  0  0  0  0  0  0  0  0  0  0  0][ 0  1  2  2  3  2  4  5  6  7  7  1  8  0][ 0  9  4  9 10  8  3  1  1  2  3 11  5  0][ 0  6 12  6  3  1  2  9  4 12  8 11  3  0][ 0  3 12 10 11 12  3  8  5  9  6  6 10  0][ 0  7  4  7 11  4  8 12  5 12  7  5  2  0][ 0 10  1  4  2 11  7  6  6 11  5  5  3  0][ 0 11  9  5  8  4  2 10  9 10 11  9  9  0][ 0 10 12  7  1 12 10  7  6  4  1  8  8  0][ 0  0  0  0  0  0  0  0  0  0  0  0  0  0]]

4、判断两个点是否可以点击消除

根据游戏规则,对一个点(x1, y1) 得到它可以直接到达的点的集合list1,所谓直接到达,指的是从(x1, y1)出发上下左右连续为0的点;对于(x2, y2)得到list2,然后判断list1中每个点和list2中每个点有没有可以直接到达(即两个点在同一行或同一列,且中间都是0),如果存在这样地点,就说明(x1, y1) 、(x2, y2)可以到达。

    def is_row_connected(self, x, y1,  y2):if y1 > y2:tmp = y1y1  = y2y2  = tmpif y2 - y1 == 1:return Truefor i in range(y1+1, y2):if self.map_matrix[x][i] != 0:return Falsereturn Truedef is_col_connected(self, x1, x2, y):if x1 > x2:tmp = x1x1 = x2x2 = tmpif x2 - x1 == 1:return Truefor i in range(x1+1, x2):if self.map_matrix[i][y] != 0:return Falsereturn Truedef get_direct_connected(self, x, y):# 同一行直接相连的点ans_list = []row =  x - 1while row >= 0:if self.map_matrix[row][y] == 0:ans_list.append([row, y])else:breakrow = row - 1row = x + 1while row < self.map_matrix.shape[0]:if self.map_matrix[row][y] == 0:ans_list.append([row, y])else:breakrow = row + 1col = y - 1while col >= 0:if self.map_matrix[x][col] == 0:ans_list.append([x, col])else:breakcol = col - 1col = y + 1while col < self.map_matrix.shape[1]:if self.map_matrix[x][col] == 0:ans_list.append([x, col])else:breakcol = col + 1return ans_listdef is_reachable(self, x1, y1, x2, y2):# 如果数字不相同,直接返回不可到达if self.map_matrix[x1][y1] != self.map_matrix[x2][y2]:return Falselist1  = self.get_direct_connected(x1, y1)list2  = self.get_direct_connected(x2, y2)for x1,y1 in list1:for x2,y2 in list2:if x1 == x2:if self.is_row_connected(x1, y1, y2):return Trueelif y1 == y2:if self.is_col_connected(x1, x2, y1):return Truereturn False

5、扫描整个矩阵,进行点击消除

由于数量比较少,可以直接枚举消除;

pymouse的使用环境主要需要**PyHook(下载对应python版本的)和PyUserInput(pip install PyUserInput即可)**两个,而且需要先下载PyHook再下载PyUserInut。安装博客: https://blog.csdn.net/dianmomanxue/article/details/95044676

PyHook和PyUserInput都下载好了后:

pip install python-xlib(安装pymouse必须要xlib的支持)

pip install pym

 '''依次点击(x1, y1) (x2, y2), 并且将这两个位置的数字变成0'''def click_and_set0(self, x1, y1, x2, y2):# 确定需要点击的坐标的中心位置c_x1 =  int(self.base_x + (y1 - 1)*self.width + self.width/2)c_y1 =  int(self.base_y + (x1 - 1)*self.width + self.width/2)c_x2 =  int(self.base_x + (y2 - 1)*self.width + self.width/2)c_y2 =  int(self.base_y + (x2 - 1)*self.width + self.width/2)time.sleep(self.click_time)self.mouse.click(c_x1, c_y1)time.sleep(self.click_time)self.mouse.click(c_x2, c_y2)# 矩阵中设为0self.map_matrix[x1][y1] = 0self.map_matrix[x2][y2] = 0'''扫描整个矩阵,并点击相消'''def scan_game(self):row_num = self.map_matrix.shape[0]col_num = self.map_matrix.shape[1]# self.click_and_set0(1,5,1,7)print(row_num)print(col_num)for i in range(1, row_num):for j in range(1, col_num):if self.map_matrix[i][j] == 0:continuefor l in range(1, row_num):for k in range(1, col_num):if i == l and j == k:continueif self.map_matrix[l][k] == 0:continueif self.is_reachable(i, j, l, k):self.click_and_set0(i, j, l, k)# 开始游戏!def start(self):# 获取图像矩阵animal_images = self.screen_grab()# 获取图标的数字矩阵self.num_matrix = self.image2num(animal_images)# 四周添加上0,做成地图矩阵self.map_matrix = np.zeros((self.num_matrix.shape[0] + 2, self.num_matrix.shape[1] + 2), dtype=np.uint32)# print(map_matrix.shape)self.map_matrix[1:9, 1:13] = self.num_matrixprint(self.map_matrix)self.scan_game()self.scan_game() # 很不优雅地扫描两遍,不过数据量小,没有关系

6、 完整代码

https://github.com/ScorpioWZ/4399game

4399小游戏—宠物连连看经典版2—游戏辅助脚本相关推荐

  1. 「趣味连连看经典版」童年经典火影忍者连连看,无需下载,在线可玩~

    前言 关注我,一起玩游戏吧! <趣味连连看经典版>还原童年经典连连看小游戏 任何年龄段都能玩的小游戏,休闲解压必备!如果你学Python学累了,玩一下这个游戏放松下吧! 一.效果展示 完整 ...

  2. 微信小程序实现window经典的扫雷游戏

    文章目录 前言 一.扫雷游戏规则是什么? 二.开发前准备 1.创建小程序项目 2.开始开发 2.1.实现网格地图 2.2.生成雷 2.3.生成雷数 2.4.长按添加雷的标识 2.5.点击网格事件 2. ...

  3. 经典版扫雷游戏的实现(含展开)

    扫雷作为经典的电脑游戏,曾令很多人着迷,今天我就用C语言实现这个游戏 扫雷的基本规则 扫雷的详细规则是随便点开一个方格,根据展开方格的数字去推断其相邻九宫格内未展开方格下面是否是地雷,最终任务就是点开 ...

  4. 街机飞机游戏合集_建立游戏引擎,经典街机游戏的转折以及更多游戏新闻

    街机飞机游戏合集 您好,开放游戏迷! 在本周的版本中,我们将了解开发开源游戏引擎的好处,结合了三个街机经典的新版本以及更多内容! 公开游戏摘要:2015年4月11日至18日 Pacapong:Pong ...

  5. 3d数学基础:图形和游戏开发(第2版)_游戏引擎编程需要哪些基本数学知识?

    现今,想要从头写一个功能强大的3D引擎,个人的力量恐怕难以胜任,即使能力足够,时间恐怕也不允许.在这个美好的开源时代,你只需具备修改各种引擎的能力便足以满足开发游戏的各项需求.现代游戏引擎的复杂级别已 ...

  6. 基于python的2048游戏设计_Python经典练习,游戏2048实现思路

    今天教大家弄一个Python版-控制台的2048,正好熟悉下Python语法,程序运行效果如下: 程序代码加上注释大概150行左右,利用了一些Python内置数据类型的操作节省了不少代码量.下面说说我 ...

  7. html5弹球游戏的实现,html5版弹球游戏

    按住鼠标左键拖动方块,避免小球撞到你 1.[代码][HTML]代码 //var stage = new createjs.Stage("canvas"); $(function() ...

  8. c语言横版格斗游戏,2.5D横版格斗游戏DEMO附素材

    内部_画十字 (颜色值转换 (#艳青, -1), 逻辑_玩家坐标2D) ' 2,显示纹理边框 ' 引擎.画线 (逻辑_玩家动画显示坐标.x, 逻辑_玩家动画显示坐标.y, 逻辑_玩家动画显示坐标.x ...

  9. 不愧是大佬用Python做一个游戏辅助脚本,完整编程思路分享!

    一.说明 简述:本文将以4399小游戏< 宠物连连看经典版2 >作为测试案例,通过识别小图标,模拟鼠标点击,快速完成配对.对于有兴趣学习游戏脚本的同学有一定的帮助.文末有Python资料和 ...

最新文章

  1. vc2010解决方案项目编译顺序_科学网—VS2012 (2008,2010) 编译问题解决合集 - 冯博远的博文...
  2. ensp大型网络环境设计与实现_mongodb内核源码设计实现、性能优化、最佳运维系列-网络传输层模块源码实现三...
  3. RemoteFX原理简介
  4. VS2013 VS2015 VS2017调试出现无法启动iis express web服务器
  5. Java8 的 Stream 流式操作之王者归来
  6. 《系统集成项目管理工程师》必背100个知识点-10项目可行性研究阶段
  7. 最详细的linux下的磁盘分区及格式化
  8. 如何在QQ浏览器查看默认搜索引擎
  9. 收集灵感必备|文字这样组合排版那才叫设计
  10. 华为有意向西方公司出售 5G 技术;iOS 13 被爆漏洞;GNOME 3.34 正式发布| 极客头条...
  11. ICLR 2019提交截止,近1600篇论文已全部上线
  12. LINUX下载编译nasm
  13. SVN相关问题(svn:E175002错误)的解决方案
  14. Excel宏编程,给出2列进行去重合并
  15. 做前端网页需要用网页框架吗
  16. mysql生日提醒_生日提醒为我所有的用户mysql
  17. 【YOLOv5实战2】基于YOLOv5的交通标志识别系统-自定义数据集
  18. couldn't find mysql server_MySQL安装错误Couldn't find MySQL server
  19. 穆利堂[推荐] WxPM信息化整体解决方案-河南郑州房地产工程项目管理系统软件 穆穆-movno1
  20. h3cr4900g3安装系统_H3C服务器安装Windows操作系统

热门文章

  1. PJzhang:搜索引擎高级语法与渗透测试
  2. Linux配置自动获取ip方式和静态ip方
  3. 为什么人人都是颜值控?
  4. daimayuan每日一题#810 最短路计数
  5. 【测试】测试人员部署测试环境是什么意思
  6. 1750套工装夹具检具治具机械设计机构solidworks模型3d课程图纸sw
  7. 渗透测试-CTF_AWD专题篇
  8. WSL2 Ubuntu18.04 apt-get update失败
  9. 送给1985年的朋友 ZT
  10. 7080mt安装linux网卡驱动,Intel英特尔PRO100/1000/10GbE系列网卡驱动