Visual C++扫雷游戏实战三:核心算法设计与实现(附源码和资源)

需要源码和资源请点赞关注收藏后评论区留言私信~~~

在前面的博客中已经讲解了扫雷游戏的菜单和各种对话框的实现,下面将对扫雷游戏的核心算法设计与实现进行讲解

一、新游戏处理模块的设计与实现

新游戏处理模块主要负责游戏中的游戏初始化以及开始游戏,其设计比较简单,只需要通过如下几个步骤即可实现

1:载入图片资源和配置文件中的数据

2:把所有的游戏参数进行初始化,例如当前消耗时间和状态等

3:初始化表示地雷区域的二维数组

4:让地雷区域图像失效,重新绘制新的图像

实现代码如下

void CMyMine::LoadConfig()
{
	char pszTmp[128] = {0};
	
	GetPrivateProfileString("HERO", "time", "0", 
		pszTmp, 127, ".\\hero.ini");
	m_uHighTime = atoi(pszTmp);

	m_uXNum		= 30;	//X坐标上的方块个数
	m_uYNum		= 16;	//Y坐标上的方块个数
	m_uMineNum	= 10;	//地雷个数

	m_bMarkful  = TRUE;
	m_bColorful = TRUE;
}
/*载入图片资源*/
void CMyMine::LoadBitmap()
{
	if (m_bColorful) {
		m_clrDark = COLOR_DARK_GRAY;
		m_bmpMine.DeleteObject();
		m_bmpMine.LoadBitmap(IDB_MINE_COLOR);
		m_bmpNumber.DeleteObject();
		m_bmpNumber.LoadBitmap(IDB_NUM_COLOR);
		m_bmpButton.DeleteObject();
		m_bmpButton.LoadBitmap(IDB_BTN_COLOR);
	}
	else {
		m_clrDark = COLOR_BLACK;
		m_bmpMine.DeleteObject();
		m_bmpMine.LoadBitmap(IDB_MINE_GRAY);
		m_bmpNumber.DeleteObject();
		m_bmpNumber.LoadBitmap(IDB_NUM_GRAY);
		m_bmpButton.DeleteObject();
		m_bmpButton.LoadBitmap(IDB_BTN_GRAY);
	}
}
/*初始化游戏*/
void CMyMine::InitGame()
{
	LoadBitmap();
	LoadConfig();
	m_nLeaveNum = m_uMineNum;
	m_uSpendTime = 0;
	m_uBtnState = BS_NORMAL;	//设置当前方块状态
	m_uGameState = GS_WAIT;		//设置当前游戏状态
	if (m_uTimer) {
		KillTimer(ID_TIMER_EVENT);
		m_uTimer = 0;
	}
	m_pNewMine = NULL;			//清空当前选中的小方块
	m_pOldMine = NULL;			//清空上次选中的小方块
	//初始化表示雷区的二维数组
	for (UINT i = 0; i<m_uYNum; i++) {
		for (UINT j = 0; j<m_uXNum; j++) {
			m_pMines[i][j].uRow = i;
			m_pMines[i][j].uCol = j;
			m_pMines[i][j].uState = STATE_NORMAL;
			m_pMines[i][j].uAttrib = ATTRIB_EMPTY;
			m_pMines[i][j].uOldState = STATE_NORMAL;
		}
	}
}

void CMyMine::StartGame()
{	
	InitGame();
	Invalidate();
}

 二、地雷格子模块的设计与实现

地雷格子的处理是扫雷游戏的核心内容,包括如下几个部分

1:地雷铺设模块

游戏中的地雷是随机铺设的,可以调用随机数发生函数生成随机数,利用随机数去除最大行列树,得到放置地雷行列坐标,然后分别放置地雷到不同的行和列的格子中去

2:自动打开周围不是地雷的格子

在游戏中,当玩家单击的格子周围没有地雷格子时,就需要程序自动把周围的格子自动打开来提高玩家的效率,其实现是通过递归的方法不断的打开当前格子周围地雷个数为0的格子来实现

3:获得周围地雷个数模块

在游戏中当玩家打开一个格子时,如果当前这个格子不是地雷,那么其一定是标明周围的地雷个数的格子,要实现这个功能主要是通过遍历当前格子周围的3×3范围的数组,当找到一个元素状态是地雷时,就把记录增加1,直到9个格子全部找完,这样就可以得到当前格子周围的地雷个数,代码实现如下

void CMyMine::ExpandMines(UINT row, UINT col)
{
	UINT i, j;
	UINT minRow = (row == 0) ? 0 : row - 1;
	UINT maxRow = row + 2;
	UINT minCol = (col == 0) ? 0 : col - 1;
	UINT maxCol = col + 2;
	UINT around = GetAroundNum(row, col);

	m_pMines[row][col].uState = 15 - around;
	m_pMines[row][col].uOldState = 15 - around;
	//在指定位置画出地雷
	DrawSpecialMine(row, col);
	if (around == 0) {
		for (i = minRow; i < maxRow; i++) {
			for (j = minCol; j < maxCol; j++) {
				if (!(i == row && j == col) && 
					m_pMines[i][j].uState == STATE_NORMAL
					&& m_pMines[i][j].uAttrib != ATTRIB_MINE) {
					if (!IsInMineArea(i, j)) continue;
					ExpandMines(i, j);
				}
			}
		}
	}
}
/*获得周围地雷个数*/
UINT CMyMine::GetAroundNum(UINT row, UINT col)
{
	UINT i, j;
	UINT around = 0;
	UINT minRow = (row == 0) ? 0 : row - 1;
	UINT maxRow = row + 2;
	UINT minCol = (col == 0) ? 0 : col - 1;
	UINT maxCol = col + 2;
	
	for (i = minRow; i < maxRow; i++) {
		for (j = minCol; j < maxCol; j++) {
			if (!IsInMineArea(i, j)) continue;
			if (m_pMines[i][j].uAttrib == ATTRIB_MINE) around++;
		}
	}
	return around;
}

/*得到周围格子状态*/
UINT CMyMine::GetAroundFlags(UINT row, UINT col)
{
	UINT i, j;
	UINT flags = 0;
	UINT minRow = (row == 0) ? 0 : row - 1;
	UINT maxRow = row + 2;
	UINT minCol = (col == 0) ? 0 : col - 1;
	UINT maxCol = col + 2;
	
	for (i = minRow; i < maxRow; i++) {
		for (j = minCol; j < maxCol; j++) {
			if (!IsInMineArea(i, j)) continue;
			if (m_pMines[i][j].uState == STATE_FLAG) flags++;
		}
	}
	return flags;
}

/*地雷判断*/
BOOL CMyMine::IsMine(UINT row, UINT col)
{
	return (m_pMines[row][col].uAttrib == ATTRIB_MINE);
}

/*雷区判断*/
BOOL CMyMine::IsInMineArea(UINT row, UINT col)
{
	return (row >= 0 && row < m_uYNum && col >= 0 && col < m_uXNum);
}
/*游戏结束*/
void CMyMine::Dead(UINT row, UINT col)
{
	CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
	CRect rcMineArea(MINE_AREA_LEFT, MINE_AREA_TOP, 
		MINE_AREA_LEFT + m_uXNum * MINE_WIDTH, MINE_AREA_TOP + m_uYNum * MINE_HEIGHT);
	
	UINT i, j;
	if (m_pMines[row][col].uAttrib == ATTRIB_MINE) {//打开了是地雷的格子
		for (i = 0; i < m_uYNum; i++) {
			for (j = 0; j < m_uXNum; j++) {
				m_pMines[row][col].uState = STATE_BLAST;
				m_pMines[row][col].uOldState = STATE_BLAST;
				if (m_pMines[i][j].uAttrib == ATTRIB_MINE
					&& m_pMines[i][j].uState != STATE_FLAG) {
					m_pMines[i][j].uState = STATE_MINE;
					m_pMines[i][j].uOldState = STATE_MINE;
				}
			}
		}
	}
	else {											//打开了判断错误的格子
		for (i = 0; i < m_uYNum; i++) {
			for (j = 0; j < m_uXNum; j++) {
				m_pMines[row][col].uState = STATE_ERROR;
				m_pMines[row][col].uOldState = STATE_ERROR;
				if (m_pMines[i][j].uAttrib == ATTRIB_MINE
					&& m_pMines[i][j].uState != STATE_FLAG) {
					m_pMines[i][j].uState = STATE_MINE;
					m_pMines[i][j].uOldState = STATE_MINE;
				}
			}
		}
	}
	
	InvalidateRect(rcMineArea);
	m_uBtnState = BS_DEAD;
	InvalidateRect(rcBtn);	
	m_uGameState = GS_DEAD;
	if (m_uTimer != 0) {
		KillTimer(ID_TIMER_EVENT);
		m_uTimer = 0;
	}

	AfxMessageBox("您踩到地雷了,游戏结束");

}

/*获得胜利*/
BOOL CMyMine::Victory()
{
	UINT i, j;
	CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
	
	for (i = 0; i < m_uYNum; i++) {
		for (j = 0; j < m_uXNum; j++) {
			if (m_pMines[i][j].uState == STATE_NORMAL) return FALSE;
			if (m_pMines[i][j].uState == STATE_DICEY) return FALSE;
		}
	}
	
	m_uBtnState = BS_VICTORY;
	m_uGameState = GS_VICTORY;
	Invalidate();
	if (m_uTimer != 0) {
		KillTimer(ID_TIMER_EVENT);
		m_uTimer = 0;
	}

	AfxMessageBox("恭喜您胜利了,游戏结束");

	if(m_uSpendTime < m_uHighTime)
	{
		CHeroDlg dlg;

		dlg.m_time = m_uSpendTime;

		dlg.SetWriteFlg(TRUE);

		dlg.DoModal();
	}

	return TRUE;
}
/*打开指定行列周围格子*/
void CMyMine::OpenAround(UINT row, UINT col)
{
	if (GetAroundFlags(row, col) != GetAroundNum(row, col)) return;
	UINT i, j;
	UINT around = 0;
	UINT minRow = (row == 0) ? 0 : row - 1;
	UINT maxRow = row + 2;
	UINT minCol = (col == 0) ? 0 : col - 1;
	UINT maxCol = col + 2;
	
	for (i = minRow; i < maxRow; i++) {
		for (j = minCol; j < maxCol; j++) {
			if (!IsInMineArea(i, j)) continue;
			if (m_pMines[i][j].uState == STATE_NORMAL) {
				ExpandMines(i, j);
				around = GetAroundNum(i, j);
				m_pMines[i][j].uState = 15 - around;
				m_pMines[i][j].uOldState = 15 - around;
			}
		}
	}
	// 检查是否胜利
	if (Victory()) {
		for (i = 0; i < m_uYNum; i++) {
			for (j = 0; j < m_uXNum; j++) {
				if (m_pMines[i][j].uAttrib == ATTRIB_MINE) {
					m_pMines[i][j].uState = STATE_FLAG;
					m_pMines[i][j].uOldState = STATE_FLAG;
				}
			}
		}
		m_nLeaveNum = 0;
		Invalidate();
	}
}

BOOL CMyMine::ErrorAroundFlag(UINT row, UINT col)
{
	if (GetAroundFlags(row, col) != GetAroundNum(row, col)) return FALSE;
	UINT i, j;
	UINT minRow = (row == 0) ? 0 : row - 1;
	UINT maxRow = row + 2;
	UINT minCol = (col == 0) ? 0 : col - 1;
	UINT maxCol = col + 2;
	
	for (i = minRow; i < maxRow; i++) {
		for (j = minCol; j < maxCol; j++) {
			if (!IsInMineArea(i, j)) continue;
			if (m_pMines[i][j].uState == STATE_FLAG) {
				if (m_pMines[i][j].uAttrib != ATTRIB_MINE) {
					Dead(i, j);
					return TRUE;
				}
			}
		}
	}
	
	return FALSE;
}

void CMyMine::OnLRBtnDown(UINT row, UINT col)
{
	UINT i, j;
	UINT minRow = (row == 0) ? 0 : row - 1;
	UINT maxRow = row + 2;
	UINT minCol = (col == 0) ? 0 : col - 1;
	UINT maxCol = col + 2;
	
	for (i = minRow; i < maxRow; i++) {
		for (j = minCol; j < maxCol; j++) {
			if (!IsInMineArea(i, j)) continue;
			//			if (i == row && j == col) continue;
			if (m_pMines[i][j].uState == STATE_NORMAL) {
				m_pMines[i][j].uState = STATE_EMPTY;
			}
			else if (m_pMines[i][j].uState == STATE_DICEY) {
				m_pMines[i][j].uState = STATE_DICEY_DOWN;
			}
		}
	}
}

void CMyMine::OnLRBtnUp(UINT row, UINT col)
{
	UINT i, j;
	UINT minRow = (row == 0) ? 0 : row - 1;
	UINT maxRow = row + 2;
	UINT minCol = (col == 0) ? 0 : col - 1;
	UINT maxCol = col + 2;
	
	for (i = minRow; i < maxRow; i++) {
		for (j = minCol; j < maxCol; j++) {
			if (!IsInMineArea(i, j)) continue;
			//			if (i == row && j == col) continue;
			if (m_pMines[i][j].uOldState == STATE_NORMAL) {
				m_pMines[i][j].uState = STATE_NORMAL;
			}
			else if (m_pMines[i][j].uOldState == STATE_DICEY) {
				m_pMines[i][j].uState = STATE_DICEY;
			}
		}
	}
	//	Invalidate();

三、游戏规则模块设计与实现

游戏规则模块的实现,主要由游戏结束和游戏胜利判断函数组成,通过对游戏的结果进行判断,实现扫雷游戏的规则

BOOL CMyMine::Victory()
{
	UINT i, j;
	CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
	
	for (i = 0; i < m_uYNum; i++) {
		for (j = 0; j < m_uXNum; j++) {
			if (m_pMines[i][j].uState == STATE_NORMAL) return FALSE;
			if (m_pMines[i][j].uState == STATE_DICEY) return FALSE;
		}
	}
	
	m_uBtnState = BS_VICTORY;
	m_uGameState = GS_VICTORY;
	Invalidate();
	if (m_uTimer != 0) {
		KillTimer(ID_TIMER_EVENT);
		m_uTimer = 0;
	}

	AfxMessageBox("恭喜您胜利了,游戏结束");

	if(m_uSpendTime < m_uHighTime)
	{
		CHeroDlg dlg;

		dlg.m_time = m_uSpendTime;

		dlg.SetWriteFlg(TRUE);

		dlg.DoModal();
	}

	return TRUE;
}
/*打开指定行列周围格子*/

四、游戏绘图模块的设计与实现

在扫雷游戏中,通过绘图模块来实现地雷,格子,地雷个数,当前时间以及控制按钮等图片和信息的显示,由如下几个函数组成

void CMyMine::DrawButton(CPaintDC &dc)
{
	CDC cdc;	
	cdc.CreateCompatibleDC(&dc);
	cdc.SelectObject(m_bmpButton);	
	dc.StretchBlt(m_uBtnRect[0], 16, 24, 24, &cdc, 0, 24 * m_uBtnState, 24, 24, SRCCOPY);
	
	dc.Draw3dRect(m_uBtnRect[1], 15, 26, 26, m_clrDark, m_clrDark);
}

void CMyMine::DrawNumber(CPaintDC &dc)
{
	CDC cdc;	
	cdc.CreateCompatibleDC(&dc);
	cdc.SelectObject(m_bmpNumber);
	
	dc.Draw3dRect(16, 15, 41, 25, m_clrDark, COLOR_WHITE);
	dc.Draw3dRect(m_uNumRect[0], 15, 41, 25, m_clrDark, COLOR_WHITE);
	int num;
	// draw remaining mine numbers
	num = (m_nLeaveNum < 0) ? 11 : m_nLeaveNum / 100;
	dc.StretchBlt(17, 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
	num = (m_nLeaveNum < 0) ? -(m_nLeaveNum - num * 100) / 10 : (m_nLeaveNum - num * 100) / 10;
	dc.StretchBlt(30, 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
	num = (m_nLeaveNum < 0) ? -m_nLeaveNum % 10 : m_nLeaveNum % 10;
	dc.StretchBlt(43, 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
	// draw take seconds
	num = m_uSpendTime / 100;
	dc.StretchBlt(m_uNumRect[0], 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
	num = (m_uSpendTime - num * 100) / 10;
	dc.StretchBlt(m_uNumRect[0] + 13, 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
	num = m_uSpendTime % 10;
	dc.StretchBlt(m_uNumRect[0] + 26, 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
}

/*void CMyMine::DrawShell(CPaintDC &dc)
{
	// draw side
	dc.FillSolidRect(0, 0, m_uShellRcX[0], LINE_WIDTH_0, COLOR_WHITE);
	dc.FillSolidRect(0, 0, LINE_WIDTH_0, m_uShellRcY[0], COLOR_WHITE);
	
	// draw small shell
	dc.Draw3dRect(SHELL_S_START_X, SHELL_S_START_Y, 
		m_uShellRcX[1], SHELL_S_H, m_clrDark, COLOR_WHITE);
	dc.Draw3dRect(SHELL_S_START_X + 1, SHELL_S_START_Y + 1, 
		m_uShellRcX[1] - 2, SHELL_S_H - 2, m_clrDark, COLOR_WHITE);
	
	// draw large shell
	dc.Draw3dRect(SHELL_L_START_X, SHELL_L_START_Y,
		m_uShellRcX[1], m_uShellRcY[1], m_clrDark, COLOR_WHITE);
	dc.Draw3dRect(SHELL_L_START_X + 1, SHELL_L_START_Y + 1,
		m_uShellRcX[1] - 2, m_uShellRcY[1] - 2, m_clrDark, COLOR_WHITE);
	dc.Draw3dRect(SHELL_L_START_X + 2, SHELL_L_START_Y + 2, 
		m_uShellRcX[1] - 4, m_uShellRcY[1] - 4, m_clrDark, COLOR_WHITE);

}*/

void CMyMine::DrawMineArea(CPaintDC &dc)
{
	CDC cdc;
	cdc.CreateCompatibleDC(&dc);
	cdc.SelectObject(m_bmpMine);
	
	for (UINT i = 0; i<m_uYNum; i++) {
		for (UINT j = 0; j<m_uXNum; j++) {
			dc.StretchBlt(MINEAREA_FRAME_X + 16 * j, MINEAREA_FRAME_Y + 16 * i, 
				16, 16, &cdc, 0, 16 * m_pMines[i][j].uState, 16, 16, SRCCOPY);
		}
	}
}

void CMyMine::DrawDownNum(MINEWND* mine, UINT num)
{
	mine->uState = 15 - num;
	mine->uOldState = 15 - num;
	CRect rcMine(mine->uCol * 16, mine->uRow * 16, (mine->uCol+1) *16, (mine->uRow+1) * 16);
	InvalidateRect(rcMine);
}

void CMyMine::DrawSpecialMine(UINT row, UINT col)
{
	CRect rcMine(col * 16, row * 16, (col+1) * 16, (row+1) * 16);
	InvalidateRect(rcMine);
}

五、玩家输入模块的设计与实现

在扫雷游戏中,用的最多的就是鼠标的输入,而鼠标输入又分为鼠标左键单击和右键单击处理两种了类型

void CMyMine::OnLButtonDown(UINT nFlags, CPoint point) 
{
	CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
	CRect rcMineArea(MINE_AREA_LEFT, MINE_AREA_TOP, 
		MINE_AREA_LEFT + m_uXNum * MINE_WIDTH, MINE_AREA_TOP + m_uYNum * MINE_HEIGHT);
	
	SetCapture();			// capture the mouse cursor
	m_bClickBtn = FALSE;
	m_bLRBtnDown = FALSE;
	if (rcBtn.PtInRect(point)) {					// click in the button area
		m_bClickBtn = TRUE;
		m_uBtnState = BS_DOWN;
		InvalidateRect(rcBtn);
	}
	else if (rcMineArea.PtInRect(point)) {			// click in the mine area
		// change mine state by gamestate
		switch(m_uGameState) {						
		case GS_WAIT: 
		case GS_RUN:
			m_pNewMine = GetMine(point.x, point.y);
			if (!m_pNewMine) return;
			if (m_pNewMine->uState == STATE_NORMAL) {
				m_pNewMine->uState = STATE_EMPTY;
			}
			if (m_pNewMine->uState == STATE_DICEY) {
				m_pNewMine->uState = STATE_DICEY_DOWN;
			}
			m_pOldMine = m_pNewMine;
			break;
		case GS_DEAD: 
		case GS_VICTORY:
			return;
		}
		m_uBtnState = BS_CLICK;
		InvalidateRect(rcBtn);
		// both of the left button and the right button are pushing down 
		if (nFlags == (MK_LBUTTON | MK_RBUTTON)) {
			m_bLRBtnDown = TRUE;
			OnLRBtnDown(m_pOldMine->uRow, m_pOldMine->uCol);
		}
		InvalidateRect(rcMineArea);
	}
	else {											// click in other area
		if (m_uGameState == GS_WAIT || m_uGameState == GS_RUN) {
			m_uBtnState = BS_CLICK;
			InvalidateRect(rcBtn);
		}
	}

	CWnd::OnLButtonDown(nFlags, point);
}

void CMyMine::OnLButtonUp(UINT nFlags, CPoint point) 
{
	CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
	CRect rcMineArea(MINE_AREA_LEFT, MINE_AREA_TOP, 
		MINE_AREA_LEFT + m_uXNum * MINE_WIDTH, MINE_AREA_TOP + m_uYNum * MINE_HEIGHT);
	
	if (rcBtn.PtInRect(point)) {					// click in the button area
		if (m_bClickBtn) {
			Invalidate();
			InitGame();
		}
		else {
			if (m_uGameState == GS_WAIT || m_uGameState == GS_RUN) {
				m_uBtnState = BS_NORMAL;
				InvalidateRect(rcBtn);
			}
		}
	}
	else if (rcMineArea.PtInRect(point)) {			// click in the mine area
		CString value;
		// different process with different gamestate
		UINT around = 0;
		switch(m_uGameState) {
		case GS_WAIT: case GS_RUN:
			// first get the MINEWND which if pushing down
			m_pOldMine = GetMine(point.x, point.y);
			if (!m_pOldMine) {
				ReleaseCapture();
				return;
			}
			// do normal process
			// judge whether the lr button are both pushed down
			if (m_bLRBtnDown) {
				m_bLRBtnDown = FALSE;
				OnLRBtnUp(m_pOldMine->uRow, m_pOldMine->uCol);
				if (m_uGameState == GS_WAIT) {
					m_uBtnState = BS_NORMAL;
					Invalidate();
					ReleaseCapture();
					return;
				}
				// if the around flags number equal to the around mines number, expand.
				if (m_pOldMine->uState != STATE_FLAG) {
					OpenAround(m_pOldMine->uRow, m_pOldMine->uCol);
				}
				// check whether the MINEWND around the special MINEWND is a mine, if it is then dead.
				if (ErrorAroundFlag(m_pOldMine->uRow, m_pOldMine->uCol)) {
//					Dead(m_pOldMine->uRow, m_pOldMine->uCol);
					ReleaseCapture();
					return;
				}
			}
			else {
				WritePrivateProfileSection("ERROR", "", "E:\\log.txt");
				// start the game, init the mines area
				if (m_uGameState == GS_WAIT) {
					if (m_uTimer) {
						KillTimer(ID_TIMER_EVENT);
						m_uTimer = 0;
					}
					// the following five lines refresh the remining mine num rect immediately 
					// when click in the mine area at the first time
					m_uSpendTime = 1;
					Invalidate();
					m_uTimer = SetTimer(ID_TIMER_EVENT, 1000, NULL);
					LayMines(m_pOldMine->uRow, m_pOldMine->uCol);		// lay all the mines down 
					m_uGameState = GS_RUN;
				}
				if (m_pOldMine->uOldState == STATE_NORMAL) {
					// out log
					value.Format("%d", m_pOldMine);
					WritePrivateProfileString("ERROR", "ADD", value, "E:\\log.txt");
					value.Format("%d", m_pOldMine->uRow);
					WritePrivateProfileString("ERROR", "ROW", value, "E:\\log.txt");
					value.Format("%d", m_pOldMine->uCol);
					WritePrivateProfileString("ERROR", "COL", value, "E:\\log.txt");
					// end
					// first judge if the special MINEWND is a mine
					if (IsMine(m_pOldMine->uRow, m_pOldMine->uCol)) {
						Dead(m_pOldMine->uRow, m_pOldMine->uCol);
						ReleaseCapture();
						return;
					}
					// the special MINEWND is not a mine 
					around = GetAroundNum(m_pOldMine->uRow, m_pOldMine->uCol);
					// out log
					value.Format("%d", around);
					WritePrivateProfileString("ERROR", "AROUND", value, "E:\\log.txt");
					// end
					if (around == 0) ExpandMines(m_pOldMine->uRow, m_pOldMine->uCol);
					else DrawDownNum(m_pOldMine, around);
				}
				else if (m_pOldMine->uOldState == STATE_DICEY) {
					m_pOldMine->uState = STATE_DICEY;
				}
				if (Victory()) {
					Invalidate();
					ReleaseCapture();
					return;
				}
			}
			break;
		case GS_VICTORY: case GS_DEAD:
			ReleaseCapture();		// release the cursor
			return;
		}
		m_uBtnState = BS_NORMAL;
		Invalidate();
	}
	else {											// click in other area
		if (m_uGameState == GS_WAIT || m_uGameState == GS_RUN) {
			m_uBtnState = BS_NORMAL;
			InvalidateRect(rcBtn);
		}
	}
	
	ReleaseCapture();		// release the cursor
	CWnd::OnLButtonUp(nFlags, point);
}

void CMyMine::OnRButtonDown(UINT nFlags, CPoint point) 
{
	CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
	CRect rcMineArea(MINE_AREA_LEFT, MINE_AREA_TOP, 
		MINE_AREA_LEFT + m_uXNum * MINE_WIDTH, MINE_AREA_TOP + m_uYNum * MINE_HEIGHT);
	
	m_bLRBtnDown = FALSE;
	if (rcMineArea.PtInRect(point)) {				// point in mine area
		if (m_uGameState == GS_WAIT || m_uGameState == GS_RUN) {
			m_pNewMine = GetMine(point.x, point.y);
			if (!m_pNewMine) return;
			// both of the left button and the right button are pushing down 
			if (nFlags == (MK_LBUTTON | MK_RBUTTON)) {
				m_bLRBtnDown = TRUE;
				OnLRBtnDown(m_pNewMine->uRow, m_pNewMine->uCol);
			}
			else {
				switch(m_pNewMine->uState) {
				case STATE_NORMAL:
					m_pNewMine->uState = STATE_FLAG;
					m_pNewMine->uOldState = STATE_FLAG;
					m_nLeaveNum--;
					break;
				case STATE_FLAG:
					m_pNewMine->uState = STATE_DICEY;
					m_pNewMine->uOldState = STATE_DICEY;
					m_nLeaveNum++;
					break;
				case STATE_DICEY:
					m_pNewMine->uState = STATE_NORMAL;
					m_pNewMine->uOldState = STATE_NORMAL;
					break;
				}
			}
			Invalidate();
			// check if victory
//			Victory();
		}
	}
	
	CWnd::OnRButtonDown(nFlags, point);
}

void CMyMine::OnRButtonUp(UINT nFlags, CPoint point) 
{
	CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
	CRect rcMineArea(MINE_AREA_LEFT, MINE_AREA_TOP, 
		MINE_AREA_LEFT + m_uXNum * MINE_WIDTH, MINE_AREA_TOP + m_uYNum * MINE_HEIGHT);
	
	m_pOldMine = GetMine(point.x, point.y);
	if (!m_pOldMine) return;
	// judge whether the lr button are both pushed down
	if (m_bLRBtnDown) {
		m_bLRBtnDown = FALSE;
		OnLRBtnUp(m_pOldMine->uRow, m_pOldMine->uCol);
		if (m_uGameState == GS_WAIT) {
			m_uBtnState = BS_NORMAL;
			Invalidate();
			return;
		}
		// if the around flags number equal to the around mines number, expand.
		if (m_pOldMine->uState != STATE_FLAG) {
			OpenAround(m_pOldMine->uRow, m_pOldMine->uCol);
		}
		// check whether the MINEWND around the special MINEWND is a mine, if it is then dead.
		if (ErrorAroundFlag(m_pOldMine->uRow, m_pOldMine->uCol)) {
//			Dead(m_pOldMine->uRow, m_pOldMine->uCol);
//			ReleaseCapture();
			return;
		}
	}
	else {
		Victory();
	}
				
	CWnd::OnRButtonUp(nFlags, point);
}

 

 

 

创作不易 觉得有帮助请点赞关注收藏~~~

猜你喜欢

转载自blog.csdn.net/jiebaoshayebuhui/article/details/129462112