如何做网站路径分析,合肥哪里有做网页的地方,信息作业网站下载,泉州台商区建设局网站目录
准备
- 扫雷软件 实现思路
- 01 窗体截取
- 02 雷块分割
- 03 雷块识别
- 04 扫雷算法实现 福利#xff1a;文末有Python全套资料哦 我们一起来玩扫雷吧。用PythonOpenCV实现了自动扫雷#xff0c;突破世界记录#xff0c;我们先来看一下效果吧。 中级 - 0.74秒 …
目录
准备
- 扫雷软件 实现思路
- 01 窗体截取
- 02 雷块分割
- 03 雷块识别
- 04 扫雷算法实现 福利文末有Python全套资料哦 我们一起来玩扫雷吧。用PythonOpenCV实现了自动扫雷突破世界记录我们先来看一下效果吧。 中级 - 0.74秒 3BV/S60.81 相信许多人很早就知道有扫雷这么一款经典的游显卡测试戏软件更是有不少人曾听说过中国雷圣也是中国扫雷第一、世界综合排名第二的郭蔚嘉的顶顶大名。扫雷作为一款在Windows9x时代就已经诞生的经典游戏从过去到现在依然都有着它独特的魅力快节奏高精准的鼠标操作要求、快速的反应能力、刷新纪录的快感这些都是扫雷给雷友们带来的、只属于扫雷的独一无二的兴奋点。
准备
准备动手制作一套扫雷自动化软件之前你需要准备如下一些工具/软件/环境
- 开发环境 Python3 环境 - 推荐3.6或者以上 [更加推荐Anaconda3以下很多依赖库无需安装] numpy依赖库 [如有Anaconda则无需安装] PIL依赖库 [如有Anaconda则无需安装] opencv-python win32gui、win32api依赖库 支持Python的IDE [可选如果你能忍受用文本编辑器写程序也可以]
- 扫雷软件
· Minesweeper Arbiter 下载地址必须使用MS-Arbiter来进行扫雷
好啦那么我们的准备工作已经全部完成了让我们开始吧~ 实现思路 在去做一件事情之前最重要的是什么是将要做的这件事情在心中搭建一个步骤框架。只有这样才能保证在去做这件事的过程中尽可能的做到深思熟虑使得最终有个好的结果。我们写程序也要尽可能做到在正式开始开发之前在心中有个大致的思路。
对于本项目而言大致的开发过程是这样的 完成窗体内容截取部分 完成雷块分割部分 完成雷块类型识别部分 完成扫雷算法
好啦既然我们有了个思路那就撸起袖子大力干
- 01 窗体截取
其实对于本项目而言窗体截取是一个逻辑上简单实现起来却相当麻烦的部分而且还是必不可少的部分。我们通过Spy得到了以下两点信息 class_name TMain
title_name Minesweeper Arbiter
ms_arbiter.exe的主窗体类别为TMainms_arbiter.exe的主窗体名称为Minesweeper Arbiter 注意到了么主窗体的名称后面有个空格。正是这个空格让笔者困扰了一会儿只有加上这个空格win32gui才能够正常的获取到窗体的句柄。
本项目采用了win32gui来获取窗体的位置信息具体代码如下
hwnd win32gui.FindWindow(class_name, title_name)
if hwnd:
left, top, right, bottom win32gui.GetWindowRect(hwnd)
通过以上代码我们得到了窗体相对于整块屏幕的位置。之后我们需要通过PIL来进行扫雷界面的棋盘截取。
我们需要先导入PIL库
from PIL import ImageGrab
然后进行具体的操作。
left 15
top 101
right - 15
bottom - 43rect (left, top, right, bottom)
img ImageGrab.grab().crop(rect)
聪明的你肯定一眼就发现了那些奇奇怪怪的Magic Numbers没错这的确是Magic Numbers是我们通过一点点细微调节得到的整个棋盘相对于窗体的位置。
注意这些数据仅在Windows10下测试通过如果在别的Windows系统下不保证相对位置的正确性因为老版本的系统可能有不同宽度的窗体边框。 橙色的区域是我们所需要的
好啦棋盘的图像我们有了下一步就是对各个雷块进行图像分割了~
- 02 雷块分割 在进行雷块分割之前我们事先需要了解雷块的尺寸以及它的边框大小。经过笔者的测量在ms_arbiter下每一个雷块的尺寸为16px*16px。
知道了雷块的尺寸我们就可以进行每一个雷块的裁剪了。首先我们需要知道在横和竖两个方向上雷块的数量。
block_width, block_height 16, 16blocks_x int((right - left) / block_width)blocks_y int((bottom - top) / block_height)
之后我们建立一个二维数组用于存储每一个雷块的图像并且进行图像分割保存在之前建立的数组中。
def crop_block(hole_img, x, y):x1, y1 x * block_width, y * block_heightx2, y2 x1 block_width, y1 block_height
return hole_img.crop((x1, y1, x2, y2))blocks_img [[0 for i in range(blocks_y)] for i in range(blocks_x)]for y in range(blocks_y):
for x in range(blocks_x):blocks_img[x][y] crop_block(img, x, y)
将整个图像获取、分割的部分封装成一个库随时调用就OK啦~在笔者的实现中我们将这一部分封装成了imageProcess.py其中函数get_frame()用于完成上述的图像获取、分割过程。
- 03 雷块识别
这一部分可能是整个项目里除了扫雷算法本身之外最重要的部分了。笔者在进行雷块检测的时候采用了比较简单的特征高效并且可以满足要求。
def analyze_block(self, block, location):block imageProcess.pil_to_cv(block)block_color block[8, 8]x, y location[0], location[1]# -1:Not opened# -2:Opened but blank# -3:Un initialized# Opened
if self.equal(block_color, self.rgb_to_bgr((192, 192, 192))):
if not self.equal(block[8, 1], self.rgb_to_bgr((255, 255, 255))):
self.blocks_num[x][y] -2
self.is_started True
else:
self.blocks_num[x][y] -1elif self.equal(block_color, self.rgb_to_bgr((0, 0, 255))):
self.blocks_num[x][y] 1elif self.equal(block_color, self.rgb_to_bgr((0, 128, 0))):
self.blocks_num[x][y] 2elif self.equal(block_color, self.rgb_to_bgr((255, 0, 0))):
self.blocks_num[x][y] 3elif self.equal(block_color, self.rgb_to_bgr((0, 0, 128))):
self.blocks_num[x][y] 4elif self.equal(block_color, self.rgb_to_bgr((128, 0, 0))):
self.blocks_num[x][y] 5elif self.equal(block_color, self.rgb_to_bgr((0, 128, 128))):
self.blocks_num[x][y] 6elif self.equal(block_color, self.rgb_to_bgr((0, 0, 0))):
if self.equal(block[6, 6], self.rgb_to_bgr((255, 255, 255))):# Is mine
self.blocks_num[x][y] 9elif self.equal(block[5, 8], self.rgb_to_bgr((255, 0, 0))):# Is flag
self.blocks_num[x][y] 0
else:
self.blocks_num[x][y] 7elif self.equal(block_color, self.rgb_to_bgr((128, 128, 128))):
self.blocks_num[x][y] 8
else:
self.blocks_num[x][y] -3
self.is_mine_form Falseif self.blocks_num[x][y] -3 or not self.blocks_num[x][y] -1:
self.is_new_start False
可以看到我们采用了读取每个雷块的中心点像素的方式来判断雷块的类别并且针对插旗、未点开、已点开但是空白等情况进行了进一步判断。具体色值是笔者直接取色得到的并且屏幕截图的色彩也没有经过压缩所以通过中心像素结合其他特征点来判断类别已经足够了并且做到了高效率。
在本项目中我们实现的时候采用了如下标注方式 1-8表示数字1到8 9表示是地雷 0表示插旗 -1表示未打开 -2表示打开但是空白 -3表示不是扫雷游戏中的任何方块类型
通过这种简单快速又有效的方式我们成功实现了高效率的图像识别。
- 04 扫雷算法实现
这可能是本篇文章最激动人心的部分了。在这里我们需要先说明一下具体的扫雷算法思路 遍历每一个已经有数字的雷块判断在它周围的九宫格内未被打开的雷块数量是否和本身数字相同如果相同则表明周围九宫格内全部都是地雷进行标记。 再次遍历每一个有数字的雷块取九宫格范围内所有未被打开的雷块去除已经被上一次遍历标记为地雷的雷块记录并且点开。 如果以上方式无法继续进行那么说明遇到了死局选择在当前所有未打开的雷块中随机点击。当然这个方法不是最优的有更加优秀的解决方案但是实现相对麻烦
基本的扫雷流程就是这样那么让我们来亲手实现它吧~
首先我们需要一个能够找出一个雷块的九宫格范围的所有方块位置的方法。因为扫雷游戏的特殊性在棋盘的四边是没有九宫格的边缘部分的所以我们需要筛选来排除掉可能超过边界的访问。
def analyze_block(self, block, location):block imageProcess.pil_to_cv(block)block_color block[8, 8]x, y location[0], location[1]# -1:Not opened# -2:Opened but blank# -3:Un initialized# Opened
if self.equal(block_color, self.rgb_to_bgr((192, 192, 192))):
if not self.equal(block[8, 1], self.rgb_to_bgr((255, 255, 255))):
self.blocks_num[x][y] -2
self.is_started True
else:
self.blocks_num[x][y] -1elif self.equal(block_color, self.rgb_to_bgr((0, 0, 255))):
self.blocks_num[x][y] 1elif self.equal(block_color, self.rgb_to_bgr((0, 128, 0))):
self.blocks_num[x][y] 2elif self.equal(block_color, self.rgb_to_bgr((255, 0, 0))):
self.blocks_num[x][y] 3elif self.equal(block_color, self.rgb_to_bgr((0, 0, 128))):
self.blocks_num[x][y] 4elif self.equal(block_color, self.rgb_to_bgr((128, 0, 0))):
self.blocks_num[x][y] 5elif self.equal(block_color, self.rgb_to_bgr((0, 128, 128))):
self.blocks_num[x][y] 6elif self.equal(block_color, self.rgb_to_bgr((0, 0, 0))):
if self.equal(block[6, 6], self.rgb_to_bgr((255, 255, 255))):# Is mine
self.blocks_num[x][y] 9elif self.equal(block[5, 8], self.rgb_to_bgr((255, 0, 0))):# Is flag
self.blocks_num[x][y] 0
else:
self.blocks_num[x][y] 7elif self.equal(block_color, self.rgb_to_bgr((128, 128, 128))):
self.blocks_num[x][y] 8
else:
self.blocks_num[x][y] -3
self.is_mine_form Falseif self.blocks_num[x][y] -3 or not self.blocks_num[x][y] -1:
self.is_new_start False
我们在这一部分通过检测当前雷块是否在棋盘的各个边缘来进行核的删除在核中1为保留0为舍弃之后通过generate_kernel函数来进行最终坐标的生成。
def count_unopen_blocks(blocks):count 0
for single_block in blocks:
if self.blocks_num[single_block[1]][single_block[0]] -1:count 1
return countdef mark_as_mine(blocks):
for single_block in blocks:
if self.blocks_num[single_block[1]][single_block[0]] -1:
self.blocks_is_mine[single_block[1]][single_block[0]] 1unopen_blocks count_unopen_blocks(to_visit)
if unopen_blocks self.blocks_num[x][y]:mark_as_mine(to_visit)
在完成核的生成之后我们有了一个需要去检测的雷块“地址簿”to_visit。之后我们通过count_unopen_blocks函数来统计周围九宫格范围的未打开数量并且和当前雷块的数字进行比对如果相等则将所有九宫格内雷块通过mark_as_mine函数来标注为地雷。
def mark_to_click_block(blocks):
for single_block in blocks:# Not Mine
if not self.blocks_is_mine[single_block[1]][single_block[0]] 1:
# Click-able
if self.blocks_num[single_block[1]][single_block[0]] -1:# Source Syntax: [y][x] - Converted
if not (single_block[1], single_block[0]) in self.next_steps:
self.next_steps.append((single_block[1], single_block[0]))def count_mines(blocks):count 0
for single_block in blocks:
if self.blocks_is_mine[single_block[1]][single_block[0]] 1:count 1
return countmines_count count_mines(to_visit)if mines_count block:mark_to_click_block(to_visit)
扫雷流程中的第二步我们也采用了和第一步相近的方法来实现。先用和第一步完全一样的方法来生成需要访问的雷块的核之后生成具体的雷块位置通过count_mines函数来获取九宫格范围内所有雷块的数量并且判断当前九宫格内所有雷块是否已经被检测出来。
如果是则通过mark_to_click_block函数来排除九宫格内已经被标记为地雷的雷块并且将剩余的安全雷块加入next_steps数组内。
# Analyze the number of blocks
self.iterate_blocks_image(BoomMine.analyze_block)# Mark all mines
self.iterate_blocks_number(BoomMine.detect_mine)# Calculate where to click
self.iterate_blocks_number(BoomMine.detect_to_click_block)if self.is_in_form(mouseOperation.get_mouse_point()):
for to_click in self.next_steps:on_screen_location self.rel_loc_to_real(to_click)mouseOperation.mouse_move(on_screen_location[0], on_screen_location[1])mouseOperation.mouse_click()
在最终的实现内笔者将几个过程都封装成为了函数并且可以通过iterate_blocks_number方法来对所有雷块都使用传入的函数来进行处理这有点类似Python中Filter的作用。
之后笔者做的工作就是判断当前鼠标位置是否在棋盘之内如果是就会自动开始识别并且点击。具体的点击部分笔者采用了作者为wp的一份代码从互联网搜集而得里面实现了基于win32api的窗体消息发送工作进而完成了鼠标移动和点击的操作。具体实现封装在mouseOperation.py中有兴趣可以在文末的Github Repo中查看。 项目完整代码/GitHub地址 | https://github.com/ArtrixTech/BoomMine 充电君会在第一时间给你带来最新、最全面的解读别忘了三联一波哦。 关注公众号资源充电吧 回复:Chat GPT 充电君发你免费畅享使用中文版哦 点击小卡片关注下回复IT 想要的资料全都有