讲解
扫雷,大家都玩过吧。
接下来,让我们用C语言来写个扫雷吧。
首先说明一下我们打算使用多文件使我们的代码更有条理。
第一步:简易菜单
(详解见:时间戳为种子随机数,猜数字_详解(带简易菜单))
这边我们自定义 meun() 函数来存放我们的菜单,使游戏流程更加清楚。
void meun() // 菜单
{
printf("********************\n");
printf("****** 扫雷 ******\n");
printf("****** 1.star ******\n");
printf("****** 0.end ******\n");
printf("********************\n");
}
int main()
{
int input = 0;
do {
meun(); // 打印菜单
printf("请输入......\n");
printf(":>>>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n");
game(); // 游戏过程
break;
case 0:
printf("游戏退出\n");
break;
default:
printf("输入非法字符,请重新选择!\n");
break;
}
} while (input);
return 0;
}
第二步:游戏主体
首先,我们自定义一个 game()函数来存放我们的游戏流程。
void game()
{
// 游戏流程
}
接下来,我们分析扫雷游戏。
这边,我们仔细观察,可以发现我们需要两个二维数组。
一个用来存放雷,一个展示给客户。
而这边存放雷的数组,我们用字符 ’1‘ ’0‘ 来表示是否有雷。展示给用户的则全用 ‘*’ 表示。
因为某个坐标周围有一个雷,直接在这个位置上放 1 就容易歧义。
接下来在代码中实现,就需要我们创建两个9*9二维数组。
但是如上图黄色位置,当排查的位置处于9*9的边界时,难以计算周围的雷。
为了更加便捷,我们干脆生成11*11的二维数组,外面一圈蓝色的全部存放 ‘0’ 这样就便捷许多。
void game() // 游戏过程
{
char mine[11][11] = {0}; // 存放雷的数组
char show[11][11] = {0}; // 展示给用户的数组
}
这样就有了两个二维数组,接下来我们为了方便以后修改这个代码。
我们就在头文件里创建两个关键字。一个为 9, 一个为11。
# define ROW 9
# define COL 9
# define ROWS ROW+2
# define COLS COL+2
这样我们创建数组就可以这样写了
void game() // 游戏过程
{
char mine[ROWS][COLS] = {0}; // 存放雷的数组
char show[ROWS][COLS] = {0}; // 展示给用户的数组
}
初始化棋盘
现在我们自定义一个函数将两个数组初始化为 ‘0’ 和 ‘*’
同时在头文件中声明一下,而函数的内容,我们存放在game.c中。
text.c
void game() // 游戏过程
{
char mine[ROWS][COLS] = {0};
char show[ROWS][COLS] = {0};
// 初始化棋盘
Initboard(mine, ROWS, COLS,'0');
Initboard(show, ROWS, COLS,'*');
}
game.h
# define ROW 9
# define COL 9
# define ROWS ROW+2
# define COLS COL+2
// 初始化棋盘
void Initboard(char board[ROWS][COLS],int rows,int cols, char set);
game.c
# include <stdio.h>
# include "game.h"
// 初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
展示棋盘
接下来为了使我们写代码过程中便于调试,我们先自定义一个函数展示我们的棋盘。
其实就是打印我们的二维数组。
同样分装在game.c中,当然也别忘了在game.h中声明一下。
为了直观的观察我们的棋盘的坐标,我们还需要打印除行列号。
game.c // 其他两个文件就不展示了,和初始化棋盘一样。
// 打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("--————我是分割线————--\n");
// 打印列号
for (int i = 0; i <= row; i++)
{
printf("%d ", i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i); //打印行号
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
放置雷
接下来,我们需要在棋盘中随机布置我们的雷。
很简单,我们只需要以时间戳为种子生成随机的坐标,将这个坐标的 ‘0’ 替换成 ‘1’ 就可以了。
只需要在 mine 数组中 9*9 的位置进行操作 。
但是有一个注意点,我们进行传参的依然需要使用 11*11 进行传参。
game.c
void SetMine(char board[ROWS][COLS],int row,int col)
{
int count = EASY_COUNT; // 在头文件中创建关键字 EASY_COUNT 10
// count 为布置的雷的个数
while (count)
{
int x = rand() % ROW + 1;
int y = rand() % COL + 1; // 生成一个随机的坐标
if (board[x][y] = '0')
{
board[x][y] = '1'; // 布置雷
count--;
}
}
}
统计雷
接下来我们就需要用户输入一个坐标,判断这个坐标处有没有雷。
有雷则游戏结束,没有雷则展示周围有几个雷。
所以,我们需要自定义一个统计周围雷的数量的函数。
如图可见,我们只需要将周围几个坐标加起来就行。
但是,因为我们这边数组里是字符,所以我们需要根据 ASCII码 进行转换。
可见,只需要减去8个 ‘0’ 就可以计算出我们雷的数量。
game.c
// 统计雷
int MineCount(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x][y - 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0';
}
排查雷
接下来,我们只需要使用whlie循环让客户多次输入坐标,进行判断即可。
game.c
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0; // 创建变量win来进行判断游戏是否结束,即循环是否停止
while (win < row * col - EASY_COUNT)
{
printf("请输入坐标.....\n");
printf(":>>>");
scanf("%d %d", &x, &y);
if (x >= 1 && y >= 1 && x <= 9 && y <= 9) //判断用户输入的坐标是否越界
{
if (mine[x][y] == '1')
{
printf("很遗憾你被炸死了!\n");
DisplayBoard(mine, ROW, COL); // 展示棋盘上的雷的位置
break;
}
else
{
if (show[x][y] != '*')
{
printf("该坐标已经被排查过了,无需再排查\n");
}
else
{
//统计坐标周围有几个雷
int count = MineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL); // 展示周围有几个雷
win++;
}
}
}
else
{
printf("不存在此坐标,请重新输入!\n");
}
if (win == row * col - EASY_COUNT) // 所有雷已经已排查出来
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
}
完整代码
text.c
# define _CRT_SECURE_NO_WARNINGS 1
# include <stdio.h>
# include "game.h"
void meun() // 菜单
{
printf("********************\n");
printf("****** 扫雷 ******\n");
printf("****** 1.star ******\n");
printf("****** 0.end ******\n");
printf("********************\n");
}
void game() // 游戏过程
{
char mine[ROWS][COLS] = {0}; // 存放雷
char show[ROWS][COLS] = {0}; // 排查雷
// 初始化棋盘
Initboard(mine, ROWS, COLS,'0');
Initboard(show, ROWS, COLS,'*');
// 布置雷
SetMine(mine, ROW, COL);
// 打印棋盘
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
// 排查雷
FindMine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do {
meun(); // 打印菜单
printf("请输入......\n");
printf(":>>>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n");
game(); // 游戏过程
break;
case 0:
printf("游戏退出\n");
break;
default:
printf("输入非法字符,请重新选择!\n");
break;
}
} while (input);
return 0;
}
game.h
#pragma once
#include <stdlib.h>
#include <time.h>
# define ROW 9
# define COL 9
# define ROWS ROW+2
# define COLS COL+2
# define EASY_COUNT 10
// 初始化棋盘
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);
game.c
# define _CRT_SECURE_NO_WARNINGS 1
# include <stdio.h>
# include "game.h"
// 初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
// 打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("--————我是分割线————--\n");
// 打印列号
for (int i = 0; i <= row; i++)
{
printf("%d ", i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i); //打印行号
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\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--;
}
}
}
// 统计雷
int MineCount(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x][y - 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0';
}
// 排查雷
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)
{
printf("请输入坐标.....\n");
printf(":>>>");
scanf("%d %d", &x, &y);
if (x >= 1 && y >= 1 && x <= 9 && y <= 9)
{
if (mine[x][y] == '1')
{
printf("很遗憾你被炸死了!\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
if (show[x][y] != '*')
{
printf("该坐标已经被排查过了,无需再排查\n");
}
else
{
//统计坐标周围有几个雷
int count = MineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
}
else
{
printf("不存在此坐标,请重新输入!\n");
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
}