相信扫雷大家都玩过,我记得小学微机课的时候每次都上课都玩扫雷。今天我来用C语言实现一个简单的扫雷。
因为我们要实现扫雷这个游戏,我们需要将二维数组传递到函数当中去实现二维数组的初始化以及打印等各种操作。若不明白二维数组的传参请移步这位大佬这里了解一下。C语言 二维数组作为函数参数的4种方式
首先我们要输出一个游戏菜单到玩家眼前,让玩家选择是否开始游戏,如果选择错误要重新选择。
void menu()//游戏菜单
{
printf("**************************\n");
printf("***********1.play*********\n");
printf("***********0.exit*********\n");
printf("**************************\n");
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
break;
default:
printf("\n\n输入错误请重新输入\n\n");
break;
}
} while (input);
}
根据扫雷的规则,雷的位置玩家是看不到的,所以我们要定义两个二维数组一个是给玩家看的show[ROWS][COLS]数组,一个给我们程序员看mine[ROWS][COLS]数组,是我们存放雷的数组。
#define ROW 9 //控制玩家眼中棋盘行数
#define COL 9 //控制玩家眼中棋盘列数
#define ROWS ROW+2 //控制实际棋盘行数
#define COLS COL+2 //控制实际棋盘列数
#define EASY_COUNT 10 //宏定义雷的个数
char mine[ROWS][COLS] = { 0 };//有雷的棋盘不给玩家看
char show[ROWS][COLS] = { 0 };//给玩家看的棋盘
我来解释一下:根据扫雷游戏规则,扫雷每排查一个非雷坐标,该位置会返回该非雷坐标周围雷的个数,所以我们可能用到循环、递归或者是别的方法,一般都会遍历非雷坐标周围八个数组元素。如果我们想设计一个9×9棋盘的话,当玩家排查棋盘边缘坐标的时,就会导致数组越界,所以我们需要定义一个比预期棋盘大一圈的数组以防止数组越界。
Init_board(mine, ROWS, COLS, '0');//将程序员看的棋盘初始化为字符0
Init_board(show, ROWS, COLS, '*');//将给玩家看的棋盘初始化为*
//初始化棋盘
void Init_board(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;
}
}
}
打印给玩家看的棋盘只需要是9×9棋盘就可以了,所以函数传参只需要传ROW,COL即可,这里用形参row,col代指他们。
void Print_board(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (j = 0; j <= row; j++)//打印第一行的坐标轴
{
printf("%d ",j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//在第一列打印坐标轴
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
函数初始化完后,以及棋盘打印给玩家看之后,接下来玩家要准备开始排雷了,所以我们要先有雷。
我们利用随机数来生成随机雷下标。
随机数的生成需要调用int rand(void)函数,若想要每一次rand()函数返回值都不相同的话就需要调用void srand(unsigned int)函数,并且向该函数中传入一个一直变化的数,
我们可以用时间戳来代替这个一直变化的数,用time_t time (time_t* timer)函数实现。
所以 我们需要调用以下函数
srand((unsigned int)time(NULL));
需要注意的是:srand函数实质上是对种子数的初始化,我们习惯性将它放在主函数来初始化,而且一个工程只需要定义一次就行了。
如果你不了解随机数,可以去看看大佬随机数的详细文章
你知道如何生成随机数吗?(超详细附图)
//随机布置雷的位置
void Set_board(char board[ROWS][COLS], int row, int col)//只需要在玩家眼中的棋盘中布置随机雷
{
int count = EASY_COUNT;//记住雷总数量
while (count)//直到雷布置完count为0,退出循环
{
//用随机数来随机雷的坐标
//我们只需要把随机雷放在mine[][]数组的第一行到第九行之间就可以
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
//判断一下该坐标是否布置过雷了
if (board[x][y] == '0')//非雷坐标是'0'
{
board[x][y] = '1'; //用‘1’代表雷
count--;
}
}
}
首先你要告知玩家“请选择你想排查的坐标”,接下来就是坐标的输入
printf("请输入你要排查的坐标\n");
int x = 0;int y = 0;
printf("请输入你要排查的坐标\n");
scanf("%d %d", &x, &y);
这时候就要考虑玩家输入的坐标是否合法以及该坐标是否被排查过了。
if (x >= 1 && x <= row && y >= 1 && y <= col)//坐标是否合法
在我们印象中扫雷每排查一个非雷坐标,该位置会返回该位置周围雷的个数,因为这些都是给玩家看的,我们将返回的该数字放在show[][]数组中,所以排查过的坐标就不是‘*’,所以我们只需要看看我们排查的坐标是不是‘*’,就可以知道该坐标是否排查过。
if (show[x][y] != '*')//判断一下坐标是否排查过
{
printf("该坐标排查过了\n");
continue;
}
continue语句的作用是跳过本次循环体中余下尚未执行的语句,立即进行下一次的循环条件判定,可以理解为仅结束本次循环。
注意:continue语句并没有使整个循环终止。
接下来我们再定义一个计数器来记录我们排查过几个非雷坐标,当玩家把非雷坐标全部排查完就赢了
if (win == row * col - EASY_COUNT)
//玩家赢了这不得好好奖励一下玩家(手动狗头)
//我直接666
如果排查到雷就输了
if (mine[x][y] == '1')
{
printf("\n这?就被炸死了?\n\n");
Print_board(mine, ROW, COL);//死了打印一下棋盘,让他死的明明白白
break;
}
难点就是怎么返回非雷的坐标周围雷的个数
这就要说一下为什么我们一开始把非雷定义成字符0,把雷定义成字符1,‘0‘ 和 ‘1’的ASCLL码值分别是48和49。所以非雷和雷相差1,我们可以让该非雷坐标周围8个坐标所对应的字符相加减去8个字符0,就是该非雷坐标周围雷的个数
int Get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1]
+ mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
}
下面是排查雷Find_board()完整代码
void Find_board(char mine[ROWS][COLS],char show[ROWS][COLS],int row, int col)
{
int x = 0;void
int y = 0;
int win = 0;//记录排查过并且不是雷的坐标个数
while (win < row * col - EASY_COUNT)
{
printf("请输入你要排查的坐标\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//坐标是否合法
{
if (show[x][y] != '*')//判断一下坐标是否排查过
{
printf("该坐标排查过了\n");
continue;
}
if (mine[x][y] == '1')
{
printf("\n这?就被炸死了?\n\n");
Print_board(mine, ROW, COL);
break;
}
else
{
int n = Get_mine_count(mine, x, y);
show[x][y] = n + '0';
Print_board(show, ROW, COL);
win++;
}
if (win == row * col - EASY_COUNT)
{
printf("\n你小子算你厉害,你赢了\n\n");
printf(" ** ** ** \n");
printf(" ** ** ** \n");
printf(" ** ** ** \n");
printf(" ** ** ** \n");
printf(" ** ** ** ** ** ** ** ** ** \n");
printf(" ** ** ** ** ** ** \n");
printf(" ** ** ** ** ** ** \n");
printf(" ** ** ** ** ** ** \n");
printf(" ****** ***** ****** \n");
break;
}
}
else//坐标不合法
{
printf("坐标不合法请重新输入\n");
}
}
}
#pragma once
#include
#include
#include
#define ROW 9 //控制玩家眼中棋盘行数
#define COL 9 //控制玩家眼中棋盘列数
#define ROWS ROW+2 //控制实际棋盘行数
#define COLS COL+2 //控制实际棋盘列数
#define EASY_COUNT 80 //宏定义雷的个数
//初始化棋盘
void Init_board(char board[ROWS][COLS], int row, int col, char set);
//打印棋盘
void Print_board(char board[ROWS][COLS], int row, int col);
//布置雷
void Set_board(char board[ROWS][COLS], int row, int col);
//排查雷
void Find_board(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//计算周围有几个雷
int Get_mine_count(char mine[ROWS][COLS], int row, int col);
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化棋盘
void Init_board(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 Print_board(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (j = 0; j <= row; j++)//打印第一行的坐标轴
{
printf("%d ",j);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//在第一列打印坐标轴
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
//随机布置雷的位置
void Set_board(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;//记住雷总数量
while (count)//直到雷布置完count为0,退出循环
{
//用随机数来随机雷的坐标
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
//用‘1’代表雷
//判断一下该坐标是否布置过雷了
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int Get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1]
+ mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
}
int Find_board(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)
{
printf("请输入你要排查的坐标\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//坐标是否合法
{
if (show[x][y] != '*')//判断一下坐标是否排查过
{
printf("该坐标排查过了\n");
continue;
}
if (mine[x][y] == '1')
{
printf("\n这?就被炸死了?\n\n");
Print_board(mine, ROW, COL);
break;
}
else
{
int n = Get_mine_count(mine, x, y);
show[x][y] = n + '0';
Print_board(show, ROW, COL);
win++;
}
if (win == row * col - EASY_COUNT)
{
printf("\n你小子算你厉害,你赢了\n\n");
printf(" ** ** ** \n");
printf(" ** ** ** \n");
printf(" ** ** ** \n");
printf(" ** ** ** \n");
printf(" ** ** ** ** ** ** ** ** ** \n");
printf(" ** ** ** ** ** ** \n");
printf(" ** ** ** ** ** ** \n");
printf(" ** ** ** ** ** ** \n");
printf(" ****** ***** ****** \n");
break;
}
}
else//坐标不合法
{
printf("坐标不合法请重新输入\n");
}
}
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("**************************\n");
printf("***********1.play*********\n");
printf("***********0.exit*********\n");
printf("**************************\n");
}
void game()
{
char mine[ROWS][COLS] = { 0 };//有雷的棋盘不给玩家看
char show[ROWS][COLS] = { 0 };//给玩家看的棋盘
//初始化棋盘
Init_board(mine, ROWS, COLS, '0');
Init_board(show, ROWS, COLS, '*');
//打印棋盘
//Print_board(mine, ROW, COL);
//Print_board(show, ROW, COL);
//布置雷
Set_board(mine, ROW, COL);
Print_board(show, ROW, COL);
Print_board(mine, ROW, COL);
//排查雷
Find_board(mine,show, ROW, COL);
}
void test()
{
int input = 0;
srand((unsigned int)time(NULL));
//生成随机数用到rand()函数,调用rand()函数需调用srand()函数
//但srand()函数的形参的需要一个unsigned int 类型的一直变动的数
//所以引用时间戳函数time(),time()函数的形参需要一个指针类型,所以传参NULL
do
{
menu();
printf("请选择\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
break;
default:
printf("\n\n输入错误请重新输入\n\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}