尝试挑战经典的扫雷游戏,用C++实现自己的版本
扫雷,这个经典的休闲益智游戏,曾陪伴许多人度过无数时光。玩家需要根据已翻开的方块上的数字,推断出隐藏在方格中的地雷位置,然后用标记或者揭开其他方块的方式逐渐清理出安全的区域。通过逻辑推理与快速反应,玩家能够体验到紧张刺激以及智慧的成就感。
设计游戏前先了解扫雷的基本规则,以围绕规则用C++实现:
一个扫雷盘面由许多方格(cell)组成,方格中随机分布着一定数量的雷(mine),一个格子中至多只有1雷。每个方格上都会显示数字来表示以此为中心的3*3的格子中有多少雷,胜利条件是打开所有非雷格(safe cell),失败条件是打开了一个雷格(踩雷)
先创建一个简单的游戏菜单
void menu()
{
cout << "********** MENU **********\n";
cout << "****** 1. play ******\n";
cout << "****** 0. exit ******\n";
cout << "*************************\n";
}
(1)用switch case来找到选项
(2)用do while来实现一局又一局游戏的菜单弹出
int main()
{
int input = 0;
do
{
menu();
cout << "请输入选项>";
cin>>input;
switch (input)
{
case 1:
game();
break;
case 0:
cout << "退出游戏";
break;
default:
cout << "输入错误,请重新输入";
break;
}
} while (input);
return 0;
}
面向对象的设计提供了一种更加结构化的思维方式,使得代码更加清晰、可读和可维护。它能够提高代码的可重用性和可扩展性,同时也能够增强代码的灵活性和可靠性。
class Mine
{
public:
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//找周围有多少雷
int GetMineCount(char mine[ROWS][COLS], int x, int y);
};
并创建一个扫雷对象mine1
Mine mine1;
以二维数组的形式展现棋盘
1.其中’0’元素为安全格
2.'1’元素为雷区
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
mine棋盘为被初始化且被埋雷的棋盘
show棋盘为用户每次操作棋盘时可看到的界面
包含的元素:雷的个数,棋盘大小
#define ROW 5//元素所在区域大小
#define COL 5
#define ROWS ROW + 2//棋盘大小偏大防止越界
#define COLS COL + 2
#define EASY_COUNT 5//雷的个数
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
对于mine棋盘和show棋盘的初始化
mine1.InitBoard(mine, ROWS, COLS, '0');
mine1.InitBoard(show, ROWS, COLS, '*');
代码实现
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
cout << "------------扫雷------------\n";
for (i = 0; i <= row; i++)
{
cout << i<<" ";
}
cout << "\n";
for (i = 1; i <= row; i++)
{
cout << i<<" ";
for (j = 1; j <= col; j++)
{
cout << board[i][j]<<" ";
}
cout << "\n";
}
cout << "------------扫雷------------\n";
}
上图即为向用户展示的棋盘
在棋盘中埋雷采用横纵坐标随机取值的方法
其中要调用rand()函数//其头文件为cstdlib
而对于x,y随机取值不能越界可使用如下方法
int x = rand() % row + 1;
int y = rand() % col + 1;
并且每次埋完一个雷就要减去一次总计雷的个数并再次进入循环
代码实现
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
}
count--;
}
}
由于用’1’表示的雷,用’0’表示的安全格,则可以直接 返回其周围元素的值之和再减去8个’0’即可
代码实现
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
return mine[x + 1][y] + mine[x - 1][y] +
mine[x][y + 1] + mine[x][y - 1] +
mine[x + 1][y + 1] + mine[x - 1][y + 1] +
mine[x - 1][y - 1] + mine[x + 1][y - 1] - 8 * '0';
}
排查雷一共分三种情况
1.第一种是排查到show中的当前元素 不是*时,说明已经翻过此格子,需要重新排查
2.第二种是排查到mine中的当前元素是’1’时,那就是找到了炸弹,游戏结束
3.第三种就是排查到mine中的当前元素是’0’时,找到他周围有多少个雷,并在show中的此位置返回个数值。
要定义一个 count2来进行计数,如果count2的个数达到安全格的个数时,则说明游戏胜利
注意:在每次翻完格子或者在游戏结束时,都要添加打印函数来向用户展示实时情况
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count2 = 0;
while (count2 < row * col - EASY_COUNT)
{
cout << "请输入横坐标>";
cin>>x;
cout << "请输入纵坐标>";
cin >> y;
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] != '*')
{
cout << "输入坐标重复,请重新输入\n";
}
else if (mine[x][y] == '1')
{
cout << "笨猪你被炸死了\n";
DisplayBoard(mine, ROW, COL);
break;
}
else
{
//不是雷,就统计x,y坐标周围有几个雷
int c = GetMineCount(mine, x, y);
show[x][y] = c + '0';
DisplayBoard(show, ROW, COL);
count2++;
}
}
else
{
cout<<"输入坐标错误,请重新输入\n";
}
}
if (win == row * col - EASY_COUNT)
{
cout << "排雷成功\n";
DisplayBoard(mine, ROW, COL);
}
}
类中代码的实现
#include
#include
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define EASY_COUNT 10
using namespace std;
class Mine
{
public:
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
cout << "------------扫雷------------\n";
for (i = 0; i <= row; i++)
{
cout << i<<" ";
}
cout << "\n";
for (i = 1; i <= row; i++)
{
cout << i<<" ";
for (j = 1; j <= col; j++)
{
cout << board[i][j]<<" ";
}
cout << "\n";
}
cout << "------------扫雷------------\n";
}
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
}
count--;
}
}
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT)
{
cout << "请输入横坐标>";
cin>>x;
cout << "请输入纵坐标>";
cin >> y;
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] != '*')
{
cout << "输入坐标重复,请重新输入\n";
}
else if (mine[x][y] == '1')
{
cout << "笨猪你被炸死了\n";
DisplayBoard(mine, ROW, COL);
break;
}
else
{
//不是雷,就统计x,y坐标周围有几个雷
int c = GetMineCount(mine, x, y);
show[x][y] = c + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else
{
cout<<"输入坐标错误,请重新输入\n";
}
}
if (win == row * col - EASY_COUNT)
{
cout << "排雷成功\n";
DisplayBoard(mine, ROW, COL);
}
}
//找周围有多少雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
return mine[x + 1][y] + mine[x - 1][y] +
mine[x][y + 1] + mine[x][y - 1] +
mine[x + 1][y + 1] + mine[x - 1][y + 1] +
mine[x - 1][y - 1] + mine[x + 1][y - 1] - 8 * '0';
}
};
把棋盘的实现步骤按顺序存放进新的函数game()中,便于 菜单的调用。
其中包含对象的创建,和对象函数的按序调用。
void game()
{
Mine mine1;
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
mine1.InitBoard(mine, ROWS, COLS, '0');
mine1.InitBoard(show, ROWS, COLS, '*');
mine1.DisplayBoard(show, ROW, COL);
mine1.SetMine(mine, ROW, COL);
mine1.FindMine(mine, show, ROW, COL);
}
菜单及主函数的实现
int main()
{
//srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
cout << "请输入选项>";
cin>>input;
switch (input)
{
case 1:
game();
break;
case 0:
cout << "退出游戏";
break;
default:
cout << "输入错误,请重新输入";
break;
}
} while (input);
return 0;
}
此扫雷小游戏是一个较简单的实现,读者可参考后根据自身写出符合需求的更高阶的制作。