C++ 扫雷游戏
这个星期在尝试着自己写一个扫雷游戏。功能基本和windows下的功能差不多。左键单击翻开一个格子,如果没有雷则显示其周围格子中含有雷的个数,没有周围的没有雷的话,那么就扩展空白块。
扩展空白块的步骤为:1、翻开一个格子,如果检测有雷,则退出游戏;2、如果检测没有雷,那么就检测其周围的八个格子,如果周围有雷的话,显示含有雷的个数,跳到第四步;3、没有周围都没有雷的话,翻开这个格子,并且对周围的八个格子重复第二步。4、进行下一次鼠标点击检测。
扫雷游戏的难点在于如何扩展空白块和捕捉到鼠标的点击操作。
捕捉鼠标的点击操作:注意在控制台窗口中,直接运行这个程序是不能捕捉到鼠标的点击操作的,需要在打开的那个控制台窗口上面那个白色边框,右键鼠标选择属性,然后关闭快速编辑选项,之后就可以正常的捕捉到鼠标的点击操作了。
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
COORD coord, pos;
INPUT_RECORD mouseRec;
DWORD res;
ReadConsoleInput(hin, &mouseRec, 1, &res); //该函数用于读取鼠标和键盘事件
if (mouseRec.EventType == MOUSE_EVENT) //检查是否有鼠标的按键按下
{
cout << "鼠标" << endl;
if (mouseRec.Event.MouseEvent.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED) //最左边的按键按下
{
cout << "左" << endl;
}
if (mouseRec.Event.MouseEvent.dwButtonState == RIGHTMOST_BUTTON_PRESSED) //最右边的按键按下
{
cout << "右" << endl;
cout << "|>";
}
}
空白块的扩展:一般有两种方法递归和有一个标志数组。我采用的是递归实现的。
/*用于检测各点坐标点周围的八个格子种地雷的个数*/
int detect_sweeper_eight(int a, int b, int init_p[], vector<vector<int>> map_sweeper)
{
int temp_sweeper_count = 0;
for (int i = a - 1; i < a + 2; ++i) //统计点开的格子周围的地雷的个数
for (int j = b - 1; j < b + 2; ++j) //检测雷区这一块还需要进行相应的更改
{
if (i<0 || j<0 || i>init_p[0] - 1 || j>init_p[1] - 1)
{
temp_sweeper_count = temp_sweeper_count;
}
else
{
if (map_sweeper[i][j] == 1)
temp_sweeper_count++;
}
}
return temp_sweeper_count;
}
/* 这个游戏中需要对已经翻开的格子进行做了标记,这个是重点,
第一次因为没有对已经翻开的格子进行标记,导致程序进入了死循环;
第二次加入了标记,能够正确的扩展空白块,但是一直在空白块之间循环,
后来发现是那个用来标识地雷的数组没有进行引用传递。用的是值传递的方式,这样导致了无限循环,但是程序不会退出*/
void detect_sweeper(int a, int b, int init_p[], vector<vector<int>> map_sweeper, vector<vector<int>> &map_sweeper_flag)
{
int sweeper_count = 0;
int map_a = a + init_p[2];
int map_b = b + init_p[3];
sweeper_count = detect_sweeper_eight(a, b, init_p, map_sweeper);
if (!sweeper_count) //如果点开的格子周围都没有地雷的话,那么就把这九个空格给消去
{
locate(map_b, map_a);
cout << " ";
map_sweeper_flag[a][b] = 0;
for (int i = a - 1; i < a + 2; ++i)
for (int j = b - 1; j < b + 2; ++j)
{
if ((i == a && j == b) || i<0 || j<0 || i>init_p[0] - 1 || j>init_p[1] - 1) //是中心点、到达边界
{
}
else
{
if (map_sweeper_flag[i][j] == 0){} //已经翻开的格子进行做了标记,这个是重点,之前不会退出循环,后来发现是那个用来标识地雷的数组没有进行引用传递。
else //用的是值传递的方式,这样导致了无限循环
{
detect_sweeper(i, j, init_p, map_sweeper, map_sweeper_flag);
}
}
}
}
else
{
locate(map_b, map_a);
cout << sweeper_count;
map_sweeper_flag[a][b] = 0;
}
}
扫雷游戏最主要的就是这两个部分了。下面是结果截图: