目录
一、扫雷游戏介绍及其主要功能点
(1)扫雷游戏介绍
(2)扫雷游戏需要实现的功能点(主要)
二、游戏功能模块的实现
(1)雷盘大小及地雷数量的定义
(2)雷盘的初始化及打印(显示)
①雷盘的初始化函数
②雷盘的打印(显示)函数
(3)布置地雷的函数
(4)排查格子的函数
①统计格子周围地雷数量的函数
②递归排查信息为‘0’的格子周围格子信息的函数
(5)标记地雷的函数
(6)删除标记的函数
三、游戏运行的截图(主要)
(1)第一次排查坐标
(2)执行一次操作后,用户继续选择
(3)过程中可标记(P)或取消标记地雷
(4)游戏失败,打印mine数组显示地雷位置
(5)游戏成功,打印mine数组显示地雷位置
四、游戏实现代码
(1)头文件game.h
(2)源文件game.c
(3)源文件test.c
扫雷游戏游戏大家并不陌生,通常由一个方形区域组成,被分成若干个小方格。(如下图)
每个方格里都可能有雷,玩家需要不断排查,根据方格提供的数字信息(表示该方格周围8个方格地雷的数量),排查完所有安全的格子,获得游戏胜利。若排查到地雷,则游戏失败。
①为了游戏体验,第一次排查格子时一定不是雷
②玩家排查一个格子时,若其无雷,则自动排查其周围格子的信息,依次循环
③玩家确定某格子有雷后,可以标记该格子
④玩家可以取消之前做的标记
雷盘的实现主要采用二维数组的方法
#define ROW 9 //二维数组行数
#define COL 9 //二维数组列数
#define MINE 10 //地雷数量
#define ROWS ROW+2 //选择格子后是检测格子周围八个格子地雷的数量,当检测边缘的格子时,
#define COLS COL+2 //附近会没有其他格子,因此拓宽二维数组的大小,方便后续检测格子周围地雷数量
设置两个雷盘,一个mine数组保存地雷位置信息,一个show数组用于展示游戏过程信息给玩家
void InitBoard(char board[COLS][ROWS], int cols, int rows, char c) {
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
board[i][j] = c;
}
}
}
游戏过程中不断打印show数组展示给玩家游戏信息
void PrintBoard(char board[COLS][ROWS], int col, int row) {
printf("---------------------------------\n\n");
printf(" "); //行坐标格式对齐
for (int i = 1; i < col + 1; i++) { //最上方一行打印列坐标0-9
printf("%d ", i);
}
printf("\n\n");
for (int i = 1; i <= col; i++) {
printf("%d ", i); //每行打印元素前打印行坐标
for (int j = 1; j <= row; j++) {
printf("%c ", board[i][j]);
}
printf("\n\n");
}
printf("---------------------------------\n\n\n");
}
采取随机值的办法布置地雷,在函数中加入一次排查操作,使得设置的随机值不为排查的坐标,以排除该地方是雷的可能性
void SetMine(char board[COLS][ROWS], char show[COLS][ROWS], int col, int row) {
int count = MINE; //计数器,用于放置设定的地雷数量
printf("请输入要排查的坐标(依次输入)\n\n");
while (1) //死循环防止输入坐标不合法
{
int x, y;
scanf("%d%d", &x, &y); //输入第一次要排查的坐标,需要将其设置为没有地雷
int m, n; //用于存放随机的行列坐标以放置地雷
if (x > 0 && x < row + 1 && y>0 && y < col + 1) //坐标合法
{
while (count)
{
m = rand() % row + 1; //行坐标范围1-9
n = rand() % col + 1; //列坐标范围1-9
if (board[m][n] == '0' && (m != x || n != y)) { //没有放过地雷的地方才能放雷,同时第一个选择的格子不能有雷
board[m][n] = '1'; //放置地雷
count--; //计数器减少
}
}
show[x][y] = get_mine_count(board, x, y) + '0';//show数组中传入第一次排查格子周围雷的数量,因为数组是char类型,所以需要加上'0'转为字符型的量(ASCII码)
ExpendMine(board, show, x, y);
PrintBoard(show, col, row); //打印show数组
break; //跳出while(1)死循环
}
else
{
printf("ERROR:请输入正确的坐标\n\n"); //执行死循环重新输入坐标
}
}
}
输入坐标排查该格子的信息,利用get_mine_count()函数统计该格子周围雷的信息,利用ExpendMine()函数递归遍历信息为‘0’的格子周围格子的信息
int FindMine(char mine[COLS][ROWS], char show[COLS][ROWS], int col, int row) {
PrintBoard(show, col, row); //打印show数组
while (1)
{
//1.输入排查的坐标
int m, n;
printf("请输入要排查的坐标(依次输入)\n\n");
scanf("%d%d", &m, &n);
if (m > 0 && m < row + 1 && n>0 && n < col + 1)
{
if (show[m][n] != '*' && show[m][n] != 'P') { //检查是否输入了重复坐标
PrintBoard(show, col, row); //打印show数组
printf("ERROR:这个地方排查过了,请重新输入\n\n");
}
else
{
//2.检查坐标处是否是雷
if (mine[m][n] == '1') //有雷
{
printf("\n");
PrintBoard(mine, col, row); //打印给玩家具体的地雷位置
printf("你踩到地雷了,真菜啊!\n\n");
printf("上方为地雷分布情况,1是地雷,0是安全格子\n\n");
return 0; //游戏结束,直接返回0
}
else
{
ExpendMine(mine, show, m, n);//递归探寻
PrintBoard(show, col, row); //打印show数组
return 1; //成功排查一次,返回1
}
}
}
else
{
printf("ERROR:请输入正确的坐标\n\n"); //执行死循环重新输入坐标
}
}
}
int get_mine_count(char mine[COLS][ROWS], int m, int n) {
return mine[m - 1][n] +
mine[m - 1][n - 1] +
mine[m - 1][n + 1] +
mine[m][n - 1] +
mine[m][n + 1] +
mine[m + 1][n - 1] +
mine[m + 1][n] +
mine[m + 1][n + 1] - 8 * '0';//排查坐标附近8个格子雷的数量,但数组是字符型数组,所以每个还要减去'0'(ASCII码)
}
void ExpendMine(char mine[COLS][ROWS], char show[COLS][ROWS], int m, int n) {
if (m > 0 && m < ROW + 1 && n>0 && n < COL + 1)
{
int count = get_mine_count(mine, m, n); //不是雷,利用get_mine_count函数统计周围雷的数量
if (count == 0)
{
show[m][n] = '0' + count;
for (int i = m - 1; i <= m + 1; i++) {
for (int j = n - 1; j <= n + 1; j++) {//双重循环访问为'0'的格子周围8个格子的信息
if (show[i][j] == '*') //递归出口,防止死循环
{
ExpendMine(mine, show, i, j);//递归探寻
}
}
}
}
else
{
show[m][n] = '0' + count;//show数组中传入格子周围雷的数量,因为数组是char类型,所以需要加上'0'转为字符型的量(ASCII码)
}
}
}
玩家经过排查后,可以确定某格子有地雷,则可输入合法的坐标标记地雷
void SignMine(char show[COLS][ROWS], char mine[COLS][ROWS], int col, int row) { //标记地雷的函数
PrintBoard(show, col, row); //打印show数组
int m, n;
printf("请输入标记为地雷的坐标\n\n");
while (1)
{
scanf("%d%d", &m, &n);
if (m > 0 && m < row + 1 && n>0 && n < col + 1)
{
if (show[m][n] == '*' || show[m][n] == 'P') { //只能标记未排查的格子或者重复标记
show[m][n] = 'P'; //标记有地雷的地方为'P'
PrintBoard(show, col, row);
break;
}
else {
printf("ERROR:此坐标已排查无雷,请输入正确坐标\n\n");
}
}
else
{
printf("ERROR:请输入正确的坐标\n\n");
}
}
}
若玩家游玩过程中发现之前的标记是错误的,则可输入合法的坐标,删除做过的标记
void DeleteMine(char show[COLS][ROWS], char mine[COLS][ROWS], int col, int row) {
PrintBoard(show, col, row); //打印show数组
int flag = 0;
for (int i = 1; i <= row; i++) {
for (int j = 1; j <= col; j++) { //遍历数组,如果没有已经被标记的坐标,则中止函数执行
if (show[i][j] == 'P')
{
flag = 1; //有被标记的坐标,改变flag的值
break;
}
if (flag == 1) {
break; //跳出外层循环
}
}
}
if (flag == 0) {
printf("请重新选择,没有已被标记的坐标\n\n");
return;
}
int m, n;
printf("请输入要删除地雷标记的坐标\n\n");
while (1)
{
scanf("%d%d", &m, &n);
if (m > 0 && m < row + 1 && n>0 && n < col + 1)
{
if (show[m][n] == 'P') { //只能删除已经标记过的坐标
show[m][n] = '*'; //将标记完的坐标重新置为*
PrintBoard(show, col, row);
break;
}
else {
printf("ERROR:此坐标未被标记,请输入正确坐标\n\n");
}
}
else
{
printf("ERROR:请输入正确的坐标\n\n");
}
}
}
存放符号和函数的定义声明:
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
#include
#define ROW 3 //二维数组行数
#define COL 3 //二维数组列数
#define MINE 2 //地雷数量
#define ROWS ROW+2 //选择格子后是检测格子周围八个格子地雷的数量,当检测边缘的格子时,
#define COLS COL+2 //附近会没有其他格子,因此拓宽二维数组的大小,方便后续检测格子周围地雷数量
void InitBoard(char board[COLS][ROWS],int cols,int rows,char c);//初始化二维数组函数
void PrintBoard(char board[COLS][ROWS], int col, int row);//打印二维数组函数
void SetMine(char board[COLS][ROWS], char show[COLS][ROWS], int col, int row);//设置地雷函数
int FindMine(char mine[COLS][ROWS], char show[COLS][ROWS], int col, int row);//排查地雷函数
int get_mine_count(char mine[COLS][ROWS], int m, int n);//统计周围地雷数量函数
void SignMine(char show[COLS][ROWS],char mine[COLS][ROWS],int col,int row);//用户标记地雷函数
void DeleteMine(char show[COLS][ROWS], char mine[COLS][ROWS], int col, int row);//用户删除标记函数
void ExpendMine(char mine[COLS][ROWS], char show[COLS][ROWS], int m, int n);//用于递归访问周围 格子没雷的格子周围格子的情况
定义游戏执行过程中的各种功能函数:
#include "game.h"
void InitBoard(char board[COLS][ROWS], int cols, int rows, char c) {
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
board[i][j] = c;
}
}
}
void PrintBoard(char board[COLS][ROWS], int col, int row) {
printf("---------------------------------\n\n");
printf(" "); //行坐标格式对齐
for (int i = 1; i < col + 1; i++) { //最上方一行打印列坐标0-9
printf("%d ", i);
}
printf("\n\n");
for (int i = 1; i <= col; i++) {
printf("%d ", i); //每行打印元素前打印行坐标
for (int j = 1; j <= row; j++) {
printf("%c ", board[i][j]);
}
printf("\n\n");
}
printf("---------------------------------\n\n\n");
}
void SetMine(char board[COLS][ROWS], char show[COLS][ROWS], int col, int row) {
int count = MINE; //计数器,用于放置设定的地雷数量
printf("请输入要排查的坐标(依次输入)\n\n");
while (1) //死循环防止输入坐标不合法
{
int x, y;
scanf("%d%d", &x, &y); //输入第一次要排查的坐标,需要将其设置为没有地雷
int m, n; //用于存放随机的行列坐标以放置地雷
if (x > 0 && x < row + 1 && y>0 && y < col + 1) //坐标合法
{
while (count)
{
m = rand() % row + 1; //行坐标范围1-9
n = rand() % col + 1; //列坐标范围1-9
if (board[m][n] == '0' && (m != x || n != y)) { //没有放过地雷的地方才能放雷,同时第一个选择的格子不能有雷
board[m][n] = '1'; //放置地雷
count--; //计数器减少
}
}
show[x][y] = get_mine_count(board, x, y) + '0';//show数组中传入第一次排查格子周围雷的数量,因为数组是char类型,所以需要加上'0'转为字符型的量(ASCII码)
ExpendMine(board, show, x, y);
PrintBoard(show, col, row); //打印show数组
break; //跳出while(1)死循环
}
else
{
printf("ERROR:请输入正确的坐标\n\n"); //执行死循环重新输入坐标
}
}
}
int get_mine_count(char mine[COLS][ROWS], int m, int n) {
return mine[m - 1][n] +
mine[m - 1][n - 1] +
mine[m - 1][n + 1] +
mine[m][n - 1] +
mine[m][n + 1] +
mine[m + 1][n - 1] +
mine[m + 1][n] +
mine[m + 1][n + 1] - 8 * '0';//排查坐标附近8个格子雷的数量,但数组是字符型数组,所以每个还要减去'0'(ASCII码)
}
void ExpendMine(char mine[COLS][ROWS], char show[COLS][ROWS], int m, int n) {
if (m > 0 && m < ROW + 1 && n>0 && n < COL + 1)
{
int count = get_mine_count(mine, m, n); //不是雷,利用get_mine_count函数统计周围雷的数量
if (count == 0)
{
show[m][n] = '0' + count;
for (int i = m - 1; i <= m + 1; i++) {
for (int j = n - 1; j <= n + 1; j++) {//双重循环访问为'0'的格子周围8个格子的信息
if (show[i][j] == '*') //递归出口,防止死循环
{
ExpendMine(mine, show, i, j);//递归探寻
}
}
}
}
else
{
show[m][n] = '0' + count;//show数组中传入格子周围雷的数量,因为数组是char类型,所以需要加上'0'转为字符型的量(ASCII码)
}
}
}
int FindMine(char mine[COLS][ROWS], char show[COLS][ROWS], int col, int row) {
PrintBoard(show, col, row); //打印show数组
while (1)
{
//1.输入排查的坐标
int m, n;
printf("请输入要排查的坐标(依次输入)\n\n");
scanf("%d%d", &m, &n);
if (m > 0 && m < row + 1 && n>0 && n < col + 1)
{
if (show[m][n] != '*' && show[m][n] != 'P') { //检查是否输入了重复坐标
PrintBoard(show, col, row); //打印show数组
printf("ERROR:这个地方排查过了,请重新输入\n\n");
}
else
{
//2.检查坐标处是否是雷
if (mine[m][n] == '1') //有雷
{
printf("\n");
PrintBoard(mine, col, row); //打印给玩家具体的地雷位置
printf("你踩到地雷了,真菜啊!\n\n");
printf("上方为地雷分布情况,1是地雷,0是安全格子\n\n");
return 0; //游戏结束,直接返回0
}
else
{
ExpendMine(mine, show, m, n);//递归探寻
PrintBoard(show, col, row); //打印show数组
return 1; //成功排查一次,返回1
}
}
}
else
{
printf("ERROR:请输入正确的坐标\n\n"); //执行死循环重新输入坐标
}
}
}
void SignMine(char show[COLS][ROWS], char mine[COLS][ROWS], int col, int row) { //标记地雷的函数
PrintBoard(show, col, row); //打印show数组
int m, n;
printf("请输入标记为地雷的坐标\n\n");
while (1)
{
scanf("%d%d", &m, &n);
if (m > 0 && m < row + 1 && n>0 && n < col + 1)
{
if (show[m][n] == '*' || show[m][n] == 'P') { //只能标记未排查的格子或者重复标记
show[m][n] = 'P'; //标记有地雷的地方为'P'
PrintBoard(show, col, row);
break;
}
else {
printf("ERROR:此坐标已排查无雷,请输入正确坐标\n\n");
}
}
else
{
printf("ERROR:请输入正确的坐标\n\n");
}
}
}
void DeleteMine(char show[COLS][ROWS], char mine[COLS][ROWS], int col, int row) {
PrintBoard(show, col, row); //打印show数组
int flag = 0;
for (int i = 1; i <= row; i++) {
for (int j = 1; j <= col; j++) { //遍历数组,如果没有已经被标记的坐标,则中止函数执行
if (show[i][j] == 'P')
{
flag = 1; //有被标记的坐标,改变flag的值
break;
}
if (flag == 1) {
break; //跳出外层循环
}
}
}
if (flag == 0) {
printf("请重新选择,没有已被标记的坐标\n\n");
return;
}
int m, n;
printf("请输入要删除地雷标记的坐标\n\n");
while (1)
{
scanf("%d%d", &m, &n);
if (m > 0 && m < row + 1 && n>0 && n < col + 1)
{
if (show[m][n] == 'P') { //只能删除已经标记过的坐标
show[m][n] = '*'; //将标记完的坐标重新置为*
PrintBoard(show, col, row);
break;
}
else {
printf("ERROR:此坐标未被标记,请输入正确坐标\n\n");
}
}
else
{
printf("ERROR:请输入正确的坐标\n\n");
}
}
}
游戏执行函数主体:
#include "game.h"
void menu(){ //打印菜单函数
printf("*******************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("*******************************\n");
}
void menu2() { //打印用户选择操作的菜单函数
printf("请输入操作选择(1-3)\n\n");
printf("1-----排查地雷\n\n");
printf("2-----标记地雷\n\n");
printf("3-----取消标记\n\n");
}
void game() { //游戏执行函数
char mine[ROWS][COLS]; //定义一个存放地雷信息的二维数组(‘1’代表地雷,‘0’代表无雷,采用char类型是为了两个数组统一类型方便后续操作)
char show[ROWS][COLS]; //定义一个展示格子周围地雷数量的二维数组用于游戏执行(采用char类型是因为要显示*字符代表格子未被排查)
//1.初始化+打印二维数组
InitBoard(mine, ROWS, COLS, '0'); //初始化地雷信息的二维数组,一开始全为无雷状态
InitBoard(show, ROWS, COLS, '*'); //初始化展示地雷数量的二维数组,一开始全为*
//PrintBoard(mine, ROW, COL); //打印地雷信息的二维数组
PrintBoard(show, ROW, COL); //打印地雷数量的二维数组
//2.设置地雷
SetMine(mine,show, ROW, COL); //设置地雷
//PrintBoard(mine, ROW, COL);
//PrintBoard(show, ROW, COL);
//3.排查地雷
//FindMine(mine, show, ROW, COL); //排查地雷
int choice;
while (1)
{
int win = 0; //计数器,用于判断游戏是否成功
int ret = -1; //用于接收查找函数的返回值,赋初值不能是0或1,因为0代表游戏失败,1代表排查成功
menu2(); //打印用户选择菜单
scanf("%d", &choice);
switch (choice)
{
case 1:
ret=FindMine(mine, show, ROW, COL); //排查地雷
break;
case 2:
SignMine(show, mine, ROW, COL); //标记地雷
break;
case 3:
DeleteMine(show,mine,ROW,COL); //取消标记
break;
default:
printf("ERROR:请输入1-3\n");
PrintBoard(show, COL, ROW); //打印show数组
break;
}
if (ret == 0) {
break; //再次跳出while循环
}
else if(ret==1){ //成功排查一次
for (int i = 1; i <= ROW; i++) {
for (int j = 1; j <= COL; j++) { //遍历show数组
if (show[i][j] == '*' || show[i][j] == 'P') { //统计show数组中*和P的数量
win += 1;
}
}
}
if (win==MINE) //如果show数组中*和P的数量正好等于雷数,则游戏成功
{
printf("游戏成功,你有点东西的!\n\n");
PrintBoard(show, COL, ROW); //打印show数组
break; //再次跳出while循环
}
}
}
}
int main() {
int input = 0;
srand((unsigned int)time(NULL)); //随机数生成器
do //游戏执行循环体
{
menu(); //打印菜单
printf("请选择>\n\n");
scanf("%d", &input);
printf("\n");
switch (input)
{
case 0:
printf("退出游戏成功!\n");
break;
case 1:
game();//执行游戏
break;
default:
printf("请输入0或1\n\n");
break;
}
} while (input); //当input=0时退出循环
return 0;
}
以上为C语言实现扫雷小游戏的全部内容,感谢您花费宝贵的时间阅读本文章!