Windows扫雷的设计思路与实现

      最近花了个把星期左右把Windows xp的扫雷实现了一下,在这里写下设计思路与大概实现。这个是下载地址,

欢迎大家下载试玩,http://kuai.xunlei.com/d/LTACJFIRFVKB。其实其中还有有个隐藏的bug。看看你能不能发现,哈哈。源代码我以后会补上,暂时不能上传,请各位谅解!

     既然是游戏的话,只有会玩了才有可能知道是怎么实现的!数字n代表以它为中心3*3的方格范围内

有n个雷。主要就是根据这个来判断是否有雷。然后是空格子的话就代表它周围没有雷。然后就是鼠标的各种事件了。下面这个是一个列表。

之后主要是按照这个写的,也有些改动,但是不大。

1.左键按下
 1.1.右键按下
  3*3方格变形
 1.2.右键未被按下
  当前方格变形
LBtnDown = TRUE;
2.右键按下
 2.1.左键按下
  3*3方格变形
 2.1.左键未被按下
  当前方格变形
RBtnDown = TRUE;
3.左键弹起
 3.1.之前左右键按下
  3.1.1.该数字的雷被找到
   调用展开算法 右键弹起失效
   RBtnDown = FALSE;
  3.1.2.没找到
   3*3方格复原 右键弹起失效

   标记标多了也会调用展开算法
   RBtnDown = FALSE;  
 3.2.之前左键按下
  当前位置方格调用展开算法
LBtnDown = FALSE;
4.右键弹起
 4.1.之前左右键按下
  4.1.1.该数字的雷被找到
   调用展开算法 左键弹起失效
   LBtnDown = FALSE;
  4.1.2.没找到
   3*3方格复原 左键弹起失效

 标记标多了也会调用展开算法
   LBtnDown = FALSE;
RBtnDown = FALSE;
5.鼠标滚动
 5.1.左键按下
  5.1.1.右键按下
   3*3方格移动
  5.1.2.右键弹起
   一个方格移动

这个是雷区初始化的代码。主要就是先写边界,然后把前面n个元素改成雷,最后随机分布。

 

void CMineView::SetMine()
{
	int i, j, currenti, currentj, i1, i2, j1, j2;

	currenti = m_mine.minenumber % m_mine.width;
	if (currenti == 0) {//刚好除尽
		currenti = m_mine.minenumber/m_mine.width;
		currentj = m_mine.width;
		for (i = 1; i <= currenti; i++) {
			for (j = 1; j <= m_mine.width; j++) {
				m_mineMax[i][j] = -1;//初始化雷区
			}
		}
		currenti++;
		currentj = 1;
	} else {//除不尽
		currenti = (m_mine.minenumber/m_mine.width)+1;
		currentj = m_mine.minenumber%m_mine.width;
		for (i = 1; i < currenti; i++) {
			for (j = 1; j <= m_mine.width; j++) {
				m_mineMax[i][j] = -1;//初始化雷区
			}
		}
		for (j = 1; j <= currentj; j++) {
			m_mineMax[currenti][j] = -1;//初始化雷区
		}
		currentj++;
	}

	//初始化不是雷区的区域
	for (j = currentj; j <= m_mine.width; j++) {
		m_mineMax[currenti][j] = 0;
	}
	++currenti;
	for (i = currenti; i <= m_mine.height; i++) {
		for (j = 1; j <= m_mine.width; j++) {
			m_mineMax[i][j] = 0;
		}
	}

	srand(time(NULL));
	for (i = m_mine.height * m_mine.width - 1; i >= 2; --i) {//布雷
		currenti = rand() % i;//产生的随机位置
		if (i%m_mine.width == 0) {//除的尽
			i1 = i / m_mine.width;
			j1 = m_mine.width;
		} else {//除不尽
			i1 = (i / m_mine.width) + 1;
			j1 = i % m_mine.width;
		}

		if (currenti == 0) {//就是第一个
			i2 = 1;
			j2 = 1;
		} else {
			if (currenti%m_mine.width == 0) {//除的尽
				i2 = currenti / m_mine.width;
				j2 = m_mine.width;
			} else {//除不尽
				i2 = (currenti / m_mine.width) + 1;
				j2 = currenti % m_mine.width;
			}
		}
		Swap(&m_mineMax[i1][j1], &m_mineMax[i2][j2]);
	}
}


   展开算法的主要思路就是用一个大的数组(包括虚拟边界)保存该算法对每一个方格的访问次数的计数。

==1就不在访问,分8个方向递归探测。碰到雷或者边界就返回。具体代码如下:

 

//8个方向探测
void CMineView::StretchMine(int i, int j)
{
	if (m_mineVisit[i][j] ==  1) {//走过的路不再走
		return ;
	}
	if (m_mineMax[i][j] == -2) {//碰壁了
		return ;
	}
	if (m_mineMax[i][j] == -1) {//撞雷了
		return ;
	} else {//上右下左递归探测
		if (m_minePic[i][j] != 1) {
			m_minePic[i][j] = 15 - m_mineMax[i][j];//根据位图中数字的关系得到的公式
		} else {
			m_minePic[i][j] = 4;
		}
		if (m_mineMax[i][j] > 0) {//碰到了数字 就停止
			m_mineVisit[i][j] = 1;//已访问
			return ;
		}
		m_mineVisit[i][j] = 1;//只有走得通的路才会被访问,访问次数+1
		StretchMine(i-1, j-1);
		StretchMine(i-1, j);//上边
		StretchMine(i-1, j+1);
		StretchMine(i, j+1);//右边
		StretchMine(i+1, j);//下边
		StretchMine(i+1, j-1);
		StretchMine(i+1, j+1);

之前碰到困难就是各个类直接自定义消息所用的句柄的获取,最难的那个就是模态对话框和CMainFrame类的通信,最后发现,模态对话框在DoModal

之前会调用oninitdialog,在oninitdialog里面可以把句柄传出来,这才实现了不同级别的绘制。然后就是绘图了,主要还是用到了双缓冲,之前双缓冲

用的很不正宗,后来改了改,用了3个cdc对象,一个选择位图,一个装图像,然后把这个最后bitblt到pDC当中。这样就避免了屏幕闪烁。

 绘图代码很固定。鼠标的各种消息会改变绘图的数据,实现了动态绘制。我感觉难点还是在于理解鼠标各个事件。我花了1天多时间,结果没

写好,之后冷静了想了下,然后把各种消息记录下来。用的多的代码写成函数方便调用。

  本身扫雷没啥难的,只要你会玩了,你就基本上知道怎么写了。就说这多把。大学已经结束了,已经要开始找工作了,这里预祝和我一样的这些毕业生

早点找到一个合适的工作!90后 up!You can make it!

你可能感兴趣的:(MFC小游戏)