"扫雷外挂" 之C++ 程序篇

上篇《 “扫雷外挂” 之 理论篇》只讲了理论,我这次来看下C++的实现。

 

0.扫雷外挂效果图

 

 

1.分析扫雷图像

 

 

下面的代码可以得到一个点的颜色,核心代码是GetPixel。

 

 

typedef struct _Color3B
{
	BYTE r;
	BYTE g;
	BYTE b;
} Color3B;
static Color3B getColorByPosition( int x, int y,HWND hWnd){
		HDC windowDC;
		RECT winRECT;      // 游戏窗口的RECT
		POINT po;          // 位置

		// 获取游戏场景
		windowDC = GetDC(hWnd);
		assert( windowDC );
		// 获得游戏窗口的RECT
		GetClientRect( hWnd, &winRECT );
		po.x = winRECT.left + x;
		po.y = winRECT.top + y;
		COLORREF retColor = GetPixel( windowDC, po.x, po.y );

		BYTE red = GetRValue(retColor);//get red;
		BYTE green = GetGValue(retColor);//get green
		BYTE blue = GetBValue(retColor);//get blue
		// 释放场景
		ReleaseDC( hWnd, windowDC );
		Color3B resultColor={red, green, blue};
		return resultColor;
	}

 

2.模拟鼠标操作

 

 

其实鼠标操作我知道的有两种,一种是下面写的这种

 

 

void CMineHelperDlg::AILClick(vector<Cell*>& vecCells){
	int kX, kY;
	::SetFocus(g_WindowHWnd);
	for(int i = 0; i < vecCells.size(); ++i){
		kX = vecCells[i]->getX() * CELL_WIDTH + GAME_LEFT + CELL_WIDTH / 2;
		kY = vecCells[i]->getY() * CELL_HEIGHT + GAME_TOP + CELL_HEIGHT / 2;
		::PostMessage( g_WindowHWnd, WM_LBUTTONDOWN, 0, MAKELPARAM(kX, kY) );
		::PostMessage( g_WindowHWnd, WM_LBUTTONUP, 0, MAKELPARAM(kX, kY) );	
	}					
}


还有种是:

 

 

SetCursorPos( 47,729);
mouse_event( MOUSEEVENTF_LEFTDOWN, 0,0,0,NULL);//鼠标down事件
mouse_event( MOUSEEVENTF_LEFTUP, 0,0,0,NULL);//鼠标up事件

 

3.扫雷外挂流程分析

 

看起来比较复杂的流程,我本来用while的,比较难搞,最后用了Goto,流程就变得非常清晰。还弄了一个struct做为标志传进去分析,看分析是什么结果,决定goto到哪个步骤。

 

typedef struct _ChangeFlag{
	bool MineChangeFlag         ;
	bool HaveClickResultFlag    ;
	bool HaveNewCombinationFlag ;
	_ChangeFlag():MineChangeFlag(false),
		HaveClickResultFlag(false),HaveNewCombinationFlag(false){

	}
	void Clear(){
		MineChangeFlag = HaveClickResultFlag = HaveNewCombinationFlag = false;
	}
}ChangeFlag;

 

 

 

 

 

 

4.分析扫雷游戏

 

来讲下最难的部分:就是上图中的进行组合分析。

 

 

a + b = 1

a + b + c = 1

要得出c = 0

 

我用一个类叫CellCombination来表示一个子项,它有N多个格子的坐标组成。还有一个Int来存总的雷数。我还记录了是哪个格子产生的子项。

 

class CellCombination{
public:
	CellCombination(int mineCount,pair<int,int> pCell):totalMineCount(mineCount),
	parentCell(pCell){

	}
	CellCombination(){
	}
	void addCell(int x, int y);
	int getSize()const;
	int getTotalMineCount()const;
	void setTotalMineCount(int mineCount);
	pair<int,int> getParentCell()const;
	bool isNearMe(CellCombination& other);

	vector<pair<int,int> >& getVecCells();
private:
	vector<pair<int,int> > vecCells;
	int totalMineCount;
	pair<int,int> parentCell;
};

 

当我们分析一个格子周围8个格子的情况时候,如果发现有多解的情况下,就产生一个子项。然后通过两两子项相减,来确定一些格子的状态。

 

 

a + b = 1

a + b + c = 1

c + d + e = 2

f + g + h = 1

如何通过两两相减呢?比如一共产生了上面4个子项。就是4个里面选2个进行操作。就是一个组合算法。可以用我上次写的算法组合算法 C++高效实现 (二进制辅助法)。如果一共有10个子项,一共有10 * 9 / 2 即45次比较。如果一共有20个子项,一共有20 * 19 / 2 即190次比较。算法效率也不是特别高。

经过实战分析后,我发现扫雷中会有很多格子数量是2的子项,比如a + b = 1,但是两个这样的子项是不会产生结果的。 所以我先对所有子项进行排序,从尾部开始往前移动,移到数量是2的就退出了。另外一个来指向当前前一个,直到第一个。

 

vector<Cell*> MainAnalysis::analysisCombinations(ChangeFlag& changeFlag){
	vector<Cell*> resultClickCells;
	sort(totalCombination.begin(), totalCombination.end(), compareCombo);
	for(int i = totalCombination.size() - 1; i >=1; --i){
		if(totalCombination[i].getSize() > 2){
			for(int j = i - 1; j >= 0; --j){
				resultClickCells = doSubThing(i,j, changeFlag);
				if(resultClickCells.size() > 0){
					return resultClickCells;
				}
			}
		}else{
			break;
		}
	}

	return resultClickCells;
}  

 


如何进行两个子项相减呢?先把教长的子项拷贝一份,然后循环短的子项中的每一个点,每找到一个结果就从长的子项的vector中删除一个。进行两个子项相减的程序就不列出来了,也做了很多优化,如果两个子项的长度是一样的,就直接退出,不会有结果的,还有如果两个子项的ParentCell距离比较远,可肯定不会有结果。如果有一个找不到就退出,也是不会有结果的。

 

 

扫雷外挂代码下载:vs2010 + MFC

http://www.waitingfy.com/wp-content/uploads/2014/04/MineHelper.zip

 

 

参考:

扫雷外挂

http://www.cnblogs.com/xiangism/archive/2012/10/27/2665130.html

http://download.csdn.net/detail/cox0902/267041

 

 

 

 

如果你觉得还不错,请顶下我的文章,谢谢!

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瓦力冫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值