话说OI搞了那么久,一些奇奇怪怪的小程序却都不会写,实在说不过去。
再加上老早就想写一个扫雷自动机了,难得现在有时间了,于是花了3天时间研究了一下。

废话不多说,先上图:

这两个是仅通过猜雷开出来的局面(看来我的估价函数还不错嘛~)

这个是通过扫雷自动机扫出来的。

附个demo

不知为什么传上去画质就渣了。。。下面附件里面有比较清楚的视频。。

本程序跑100次初级,中级,高级扫雷获胜场次如下:

  • 初级:83
  • 中级:57
  • 高级:27

虽说胜率不太高,但还是可以看的吧:-)。

最快时间(s):

  • 初级:1
  • 中级:1
  • 高级:2

说下程序怎么做的吧。
一开始我什么都不会,怎么控制鼠标移动我都不懂。
然后听说以前有人用按键精灵做了一个,我就去找,然后找到了使用读内存和输命令破解的两个按键精灵脚本。
然后我就对照着注释和按键精灵的中文翻译,大概是看懂了。然后发现好像不怎么需要命令,就打算用c++做了(虽然接下来一波三折)。
顺便贴下按键精灵的脚本。

RunApp "winmine.exe"
Delay 1000
//查找《扫雷》的窗体句柄
hwnd = Plugin.Window.Find("扫雷", "扫雷")
sRect = Plugin.Window.GetClientRect(hwnd)
Dim MyArray
MyArray=Split(sRect,"|")
Ux=MyArray(0)
Uy = MyArray(1)
MoveTo Ux+19,Uy+62
//x, y中分别保存当前扫雷的长与宽的格子数量
Dim x, y
x=Plugin.Memory.Read32Bit(hwnd,&h1005334)
y = Plugin.Memory.Read32Bit(hwnd, &h1005338)
MessageBox Ux
MessageBox Uy
/*
Dim i, j, value
For i=0 to x-1
For j=0 to y-1
Delay 10
value=Plugin.Memory.Read8Bit(hwnd,&h1005360+(i+j*32+1))
If value=&h8A or value=&h8F then
MoveTo Ux+19+i*16,Uy+62+j*16
RightDown 1
RightUp 1
Else
moveto Ux+19+i*16,Uy+62+j*16
LeftDown 1
LeftUp 1
End If
Next
Next
Delay 10
*/
EndScript

这个是用读内存做的,我稍微修改了一下,为了测试点东西。
下面是按键精灵自带的翻译(不知道高到哪里去):

诶,好像扯远了。。好吧,转入正题:
程序是用c++写的,用了winapi的东西,主要是为了做截图 以及模拟鼠标操作。
截图这边比较周折。一开始我用GetPixel,结果慢成狗。后来去网上google了半天总算弄懂了更快的办法:

HBITMAP hBitmap = CreateCompatibleBitmap(hdc1, rect.right,rect.bottom);
SelectObject(hdc2, hBitmap);
BitBlt(hdc2, 0, 0, rect.right,rect.bottom, hdc1, 0, 0, SRCCOPY);
GetObject(hBitmap, sizeof(bm), (LPSTR)&bm);
int nbyte = bm.bmBitsPixel / 8;  
  
BITMAPINFO bi;    
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);    
bi.bmiHeader.biWidth = bm.bmWidth;    
bi.bmiHeader.biHeight = -bm.bmHeight;    
bi.bmiHeader.biPlanes = 1;    
bi.bmiHeader.biBitCount = bm.bmBitsPixel;     
bi.bmiHeader.biCompression = BI_RGB;     
bi.bmiHeader.biSizeImage = bm.bmWidth * bm.bmHeight * nbyte;   
bi.bmiHeader.biClrUsed = 0;    
bi.bmiHeader.biClrImportant = 0;
GetDIBits(hdc1, hBitmap, 0, bm.bmHeight, pBits, &bi, DIB_RGB_COLORS);
for (int j = 0;j < bm.bmHeight;++j)  
    {  
        for (int i = 0;i < bm.bmWidth;++i)  
            {  
                rv[i][j] = pBits[i * nbyte + j * bm.bmWidthBytes + 2];
                gv[i][j] = pBits[i * nbyte + j * bm.bmWidthBytes + 1];
                bv[i][j] = pBits[i * nbyte + j * bm.bmWidthBytes + 0];
            }
    }
DeleteObject(hBitmap);

这样hdc1这个dc中的图片就被截下来了,存在了pBits里面。

然后为了方便我用读内存大法读取了行列格子数以及雷的个数。
接下来就是如何开格子了。
由于作者比较傻,只用了比较朴素的几种办法:
1.最朴素的那种
2.枚举两个相关的格子,看能否推出猫腻。
3.猜雷大法!(经试验,初级跑100局有18局是赢的)。

使用方法

压缩包里面有winxp的扫雷,还有编译好的exe,直接运行就好了。不能运行的话就按照说明里面重新编译。我的g++(为什么是g++?因为oi搞多了。。)版本是4.4.0。经测试,cena自带的那个g++也可以编译。
UPD1 2014年8月20日 17:04:53

程序bug较多。

最后,祝大家找bug愉快~~

附件1:制作本程序用到的大部分文件(包括视频) http://pan.baidu.com/s/1eQ49SIY
附件2:扫雷自动机http://pan.baidu.com/s/1eQ1mgcq

Comments

comments powered by Disqus
Copyright © 2013 lazycal of Shen Ben
Powered by Logdown and Greyshade
Favicon from The Noun Project