扫雷外挂程序开发

27 篇文章 0 订阅
21 篇文章 0 订阅

1. 前言

最近突然看到这篇博文: http://blog.csdn.net/zhyh1435589631/article/details/61927733,讲的是制作一个自动扫雷机器人,算法原理也讲清楚了,但是这个是基于windows xp自带的扫雷程序的,由于我们的操作系统是windows 10,已经没有这个默认的扫雷程序了,于是只能从网上直接下载一个下来进行分析,并编写我们自己的扫雷机器人程序。

2. 使用工具

开发这个项目,我们使用到了几个基本工具:
1. 扫雷程序: http://download.csdn.net/detail/zhyh1435589631/9779857
2. Resource Hacker http://download.csdn.net/detail/zhyh1435589631/9779870
3. CheatEngine
4. gtest
5. ccchecker
6. visual studio ultimate 2013 update 4
7. opencv 3.1.0
8. cmake http://download.csdn.net/detail/wenshmilyaw/9682690
9. git

3. 实现效果

这里基本上有两种实现方案:1. 模拟人点击,2.读取扫雷进程的内存。从下面的展示效果来看,模拟人点击的方式,比较基本,但是有时候会涉及到对未知区域的随机点击(无法根据已有信息推断),而另一种模式则比较暴力,简单高效。

3.1. 模拟人点击的方式进行扫雷实现成功

这里写图片描述

3.2. 模拟人点击的方式进行扫雷实现失败

这里写图片描述

3.3. 直接读取内存方式

这里写图片描述

4. 工程地址:

https://github.com/zhyh2010/AutoSweepMine

5. 开发流程

这里写图片描述

5.1 iter1

5.1.1 建立初始工程

我们一开始使用的是cmake进行工程的建立,当时主要考虑是使用这种方式建立工程,比较具有跨平台的通用性能,然而,由于我们的目标程序实际上是在windows上的,感觉其实有点画蛇添足,不过用这种方式配置opencv倒是蛮方便的。
下面给出CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
cmake_policy(SET CMP0012 NEW)
PROJECT(AutoSweepMine)
FIND_PACKAGE(OpenCV 3.0.0 REQUIRED)
MESSAGE("OpenCV version : ${OpenCV_VERSION}")
include_directories(${OpenCV_INCLUDE_DIRS})
link_directories(${OpenCV_LIB_DIR})

SET(SRC main.cpp MineGame.cpp MineGame.h unitTest/unitTest.h unitTest/unitTest.cpp)
ADD_EXECUTABLE(${PROJECT_NAME} ${SRC})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${OpenCV_LIBS})

后面使用cmake创建一下vs2013 x64工程,这样opencv就算配置成功了。

5.1.2 添加gtest测试框架

我们这里使用的是gtest测试框架 http://blog.csdn.net/zhyh1435589631/article/details/61931553
当然也可以使用cppunit : http://blog.csdn.net/zhyh1435589631/article/details/51292073
测试框架的效果是类似的,实际上就是给自己一个代码的可靠性保障,确保自己写的代码每一步都能得到预期的效果

5.1.3 查找扫雷功能

这部分的功能主要借助于Findwindow http://baike.baidu.com/link?url=TiPR2sr9poXYh0O59IWPOm4RptPsn-jOOijaeTMCVrhuaUY7nQ4l4gNvagEvNOD0ViB10m9leWzDO7VCCZ3MrSF_hfioIHyacvi2d1HgbKq
这个函数接受一个className 和 一个 windowName作为输入参数,返回一个窗口的句柄。
通过spy++我们可以很容易的得到这两个参数
这里写图片描述

5.1.4 获取扫雷客户区图像,并提取轮廓

这部分,实际上我们可以获取得到窗口对象的资源句柄HDC,将他所对应的图像保存为一个bitmap位图http://blog.csdn.net/norains/article/details/4594514,然后使用opencv的findContours提取轮廓即可。
提取出来的轮廓效果如下:
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

5.1.5 完成扫雷行数,列数,雷数的获取

显然,行数和列数比较容易获取,只需要获取扫雷区域(MineMatrixArea矩形的大小)除以相应小单元的边长就可以得到相应的行数和列数了
雷数,只需要读取左上角晶体管中数字即可,利用图像处理的方式可以比较容易的解决。然而,这部分到最后一直都没有使用,因而有些多余。

5.1.6 实现人脸表情识别

表情识别,本质上就是提取相应的轮廓区域,然后使用图像差分方式可以很容易比较得到相应的脸型状态。

5.1.7 初级扫雷算法逻辑

这个初级扫雷算法逻辑,是孤立的看待每一个雷单元

1.如果一个格子周围的确定的雷的数目等于这个格子的示数,那么这个格子周围所有还未确定的格子都是安全的。
2.如果一个格子周围所有不确定的数目等于格子的示数减去已经确定的雷的数目,那么剩余的不确定的格子一定全都是雷!

5.2 iter2

5.2.1 更新底层对雷数的判断工作

之前版本中雷数判断操作是采用将提取的区域与标准模板进行比对。然而,在性能分析的时候,我们发现这个GetMineMatrixCellStatusByRowAndCol操作被调用的频率太高,而每次调用的时候内部都要进行文件读取操作imread等,代价实在太大
这里写图片描述
这里写图片描述
这里写图片描述

于是,我们考虑到由于雷单元显示的图像就这么几种,我们可以抽取他们的特征作为判别标准,以减少io操作。
我们采用的策略是: 对样本的所有单元的BGR通道按照0, 128, 192, 255 进行投票产生一组12维度的向量来表示各个标准图形
标准特征向量如下:

static vector<vector<int>> FeatureArray{
        vector<int>{0, 54, 148, 54, 0, 54, 148, 54, 0, 54, 148, 54},
        vector<int>{0, 31, 185, 40, 40, 31, 185, 0, 40, 31, 185, 0},
        vector<int>{65, 31, 160, 0, 0, 96, 160, 0, 65, 31, 160, 0},
        vector<int>{62, 31, 163, 0, 62, 31, 163, 0, 0, 31, 163, 62},
        vector<int>{0, 87, 169, 0, 56, 31, 169, 0, 56, 31, 169, 0},
        vector<int>{70, 31, 155, 0, 70, 31, 155, 0, 0, 101, 155, 0},
        vector<int>{0, 103, 153, 0, 0, 103, 153, 0, 72, 31, 153, 0},
        vector<int>{44, 31, 181, 0, 44, 31, 181, 0, 44, 31, 181, 0},
        vector<int>{0, 107, 149, 0, 0, 107, 149, 0, 0, 107, 149, 0},
        vector<int>{221, 31, 0, 4, 221, 31, 0, 4, 77, 31, 0, 148},
        vector<int>{0, 31, 225, 0, 0, 31, 225, 0, 0, 31, 225, 0},
        vector<int>{39, 54, 109, 54, 39, 54, 109, 54, 22, 54, 109, 71},
        vector<int>{0, 54, 148, 54, 0, 54, 148, 54, 0, 54, 148, 54}
    };

依次对应(INVALID 这里没有任何对应,因为这个图不存在)

enum MineStatus{
    UNKNOWN = 0, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, MINE, SAFE, FLAG, INVALID
};

5.2.2 高级策略

这个高级策略实际上是将一个位置点与他相邻的位置点联合起来进行判断,因而效率大大提高

1.如果点(i,j)剩余的地雷数等于(i,j)关于(i’,j’)非公共区域位置的数目加上(i,j)和(i’,j’)的公共未知区域的雷的数目,那么(i,j)关于点(i’,j’)的非公共未知区域则全是雷!
2.如果点(i,j)剩余的地雷数等于(i,j)关于(i’,j’)非公共区域位置的数目,那么(i,j)关于点(i’,j’)的非公共未知区域则全部安全!

5.2.3 直接内存读取

通过分析,我们知道扫雷程序的雷区布局是这样的:
这里写图片描述
这里写图片描述
这里写图片描述
雷区始终从0x1005340开始,并且外面有一圈 0x10 标识边界, 8F 即为雷的位置所在。
知道这个之后,就可以非常容易的操作内存了。

具体操作可以参考: http://blog.csdn.net/lybwwp/article/details/8168833
这是 C# 的调用方式, C++ 类似

5.3 iter3

5.3.1 使用容器加快搜索速度

之前版本中,使用两层for循环进行遍历,每次都需要判断雷单元状态,非常耗时,这里我们使用容器记录雷单元状态,相当于建立了一个缓存机制,提高搜索速度

5.4 iter4

5.4.1 更新gamble策略

之前版本中,gamble每次都是选取最后一个未知区域,现在修改为随机选取一个未知的区域,更加符合人的行为。通过测试发现引入随机操作之后,速度大大提升。

5.4.2 添加左右键双击策略

我们正常扫雷的时候,有时候会采用同时按下左右键的方式,这样触发出来的区域会更大,这里我们也加入这样的操作,提高扫雷的速度

6. 不足

程序中有一个reset操作的bug 没有调试好,本来是想在遇到雷失败之后,点击笑脸重新开启一局游戏,但是这个新局始终无法自动开启,需要手工点击,需要进行改进。

  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值