年前无意看到一个用 Python 写的小桌面程序, 可以自动玩扫雷的游戏, 觉得挺有意思, 决定用 C# 也做一个.[真实情况是: 我知道 Python 最近比较火, 非常适合搞爬虫, 大数据, 机器学习之类的, 但现在连桌面程序都用 Python 做了吗? 还给不给. NET 程序员活路了? 简直不能忍!]
春节期间正好有闲就搞了一下, 先下载了一个第三方的扫雷游戏, 实现功能以后觉得下载的这个扫雷游戏分辨率太低了, 也不好看, 所以又自己做了一个扫雷游戏, 凑成一套.
源码下载地址: https://github.com/seabluescn/AutoMineSweeper
需要提前说明的是, 这两个程序是独立的, 之间没有任何接口与联系, 自动扫雷的程序通过读取屏幕信息获取游戏状态, 并模拟鼠标操作来进行游戏. 下面就几个相关技术点和大家分享一下.
1, 获取应用程序窗口
- [DllImport("user32.dll")]
- private static extern int GetWindowRect(IntPtr hwnd, out Rect lpRect);
- private Rect GetWindowRect()
- {
- Process[] processes = Process.GetProcesses();
- Process process = null;
- for (int i = 0; i < processes.Length - 1; i++)
- {
- process = processes[i];
- if (process.MainWindowTitle == "MineSweeper")
- {
- break;
- }
- }
- Rect rect = new Rect();
- GetWindowRect(process.MainWindowHandle, out rect);
- return rect;
- }
2, 屏幕截图
- Rect rect = GetWindowRect();
- int left = rect.Left;
- int top = rect.Top;
- int centerleft = 21; // 偏移
- int centertop = 93;
- int centerwidth = 300;
- int centerheight = 300;
- Bitmap bitmapCenter = new Bitmap(centerwidth, centerheight);
- using (Graphics graphics = Graphics.FromImage(bitmapCenter))
- {
- graphics.CopyFromScreen(left + centerleft, top + centertop, 0, 0, new Size(centerwidth, centerheight));
- this.pictureBox1.Image?.Dispose();
- this.pictureBox1.Image = bitmapCenter;
- }
截图后, 根据图片上固定位置的颜色信息判断该位置的状态, 最终形成一个数组.
3, 模拟鼠标点击
- [DllImport("user32")]
- private static extern int mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
- const int MOUSEEVENTF_MOVE = 0x0001; // 移动鼠标
- const int MOUSEEVENTF_LEFTDOWN = 0x0002; // 模拟鼠标左键按下
- const int MOUSEEVENTF_LEFTUP = 0x0004; // 模拟鼠标左键抬起
- const int MOUSEEVENTF_RIGHTDOWN = 0x0008; // 模拟鼠标右键按下
- const int MOUSEEVENTF_RIGHTUP = 0x0010; // 模拟鼠标右键抬起
- const int MOUSEEVENTF_MIDDLEDOWN = 0x0020; // 模拟鼠标中键按下
- const int MOUSEEVENTF_MIDDLEUP = 0x0040; // 模拟鼠标中键抬起
- const int MOUSEEVENTF_ABSOLUTE = 0x8000; // 标示是否采用绝对坐标
- int clickPointX = X * 65535 / Screen.PrimaryScreen.Bounds.Width;
- int clickPointY = Y * 65535 / Screen.PrimaryScreen.Bounds.Height;
- // 移动鼠标
- mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, clickPointX, clickPointY, 0, 0);
- // 左键点击
- mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
- // 右键点击
- mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
4, 游戏算法
获得游戏状态后, 需要判断下一步操作, 是点开某个位置还是右键标记某个位置, 算法循环遍历所有方块, 一共三步:
1) 基础算法
基础算法 1: 对于已经翻开的块, 中心数字和周围已经标记的雷数一致, 其周围所有未知位置都不是雷, 左键点开
基础算法 2: 对于已经翻开的块, 中心数字 = 未知位置数量 + 周围已经标记的雷数 : 其周围所有未知位置均为雷, 右键标记
2) 高一级算法
先计算所有已翻开的块, 其周围未知块含雷的数量之和.
算法 1: 对于已经翻开的块, 如果周围未知块超过 2 个, 其中有一个未知块: 中心数字 - 雷 == 其他位置块组合雷数总和: 该未知块必不是雷
算法 2: 对于已经翻开的块, 如果周围未知块超过 2 个, 其中有一个未知块: 数字 - 雷 - 其他位置块组合雷数 = 1: 该未知块必是雷
3) 实在没有找到合适的点, 只能随机点开
对所有未知的点, 计算一下周围雷的概率, 选择概率最小的点开.
经测试, 程序对目标状态的识别率为 100%, 智能程度还不错, 比一般人玩的好, 无聊时可以看它玩一天.
来源: https://www.cnblogs.com/seabluescn/p/10522627.html