我们用C 语言代码实现了一个简单的控制台版扫雷游戏,代码分为三个部分,分别是头文件中定义的函数声明以及两个源文件game.h
和test.c
,game.c。
1.头文件(game.h
)部分
首先包含了<stdio.h>
(用于标准输入输出操作,像printf
、scanf
函数的使用)、<time.h>
(获取时间相关信息,用于初始化随机数生成器)和<stdlib.h>
(包含了如rand
、srand
等函数,用于生成随机数以及内存分配等相关操作)这几个常用的标准库头文件。
接着通过#define
定义了一些宏常量:ROW
和COL
分别定义了棋盘的行数和列数(这里是 9x9 的棋盘,不包含边缘扩充部分)ROWS
和COLS
则是在原棋盘行数和列数基础上各加 2,用于在棋盘周边扩充一圈,方便后续判断某个格子周围雷的数量等操作,避免边界判断的复杂情况。EASY_COUNT
定义了简单难度下雷的数量,这里设定为 10 个雷。
最后声明了四个函数,这些函数分别用于初始化棋盘、打印棋盘、布置雷以及排查雷,它们的具体实现在game.c
文件中。
cpp
#include<stdio.h>
#include<time.h>
#include<stdlib.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);
2.源文件(game.c
)部分
(InitBoard
函数)
这个函数用于初始化棋盘,它通过两层嵌套的for
循环遍历给定的二维数组(棋盘)的每一个元素,将每个元素都设置为传入的参数set
指定的值。例如,初始化雷的棋盘时可以传入'0'
表示初始无雷,初始化显示给玩家的棋盘时可以传入'*'
表示初始未排查的格子。
(DisplayBoard
函数)
该函数用于在控制台打印出棋盘的样子。首先打印出列号作为表头,然后逐行打印行号以及对应行上每个格子的状态(用相应的字符表示,比如'*'
表示未排查、数字字符表示周围雷的数量等)。
(SetMine
函数)
此函数的功能是在棋盘上随机布置雷。它先记录还需要布置的雷的数量(初始化为EASY_COUNT
),然后通过while
循环不断生成随机的坐标(x
和y
,范围是棋盘内部有效的行列范围,通过rand
函数结合取余和加 1 操作来实现),判断该坐标位置是否还没有布置雷(是否为'0'
),如果是,则将该位置设置为'1'
表示有雷,并将剩余雷的数量减 1,直到布置完规定数量的雷为止。
(get_mine_count
函数)
这个函数用于计算给定坐标(x
,y
)周围 8 个格子中雷的数量。它通过将周围 8 个格子对应的字符值('0'
或'1'
)相加,然后减去 8 倍的'0'
字符的 ASCII 值(因为字符'0'
到'9'
的 ASCII 值是连续的,相减后可以得到对应的整数值),从而得到周围雷的实际数量。
(FindMine
函数)
该函数实现了雷的排查逻辑,是游戏的核心交互部分。它通过一个while
循环不断让玩家输入要排查的坐标,先判断输入坐标是否合法(在棋盘范围内),接着检查该坐标对应的格子在显示棋盘show
中是否已经排查过(判断是否不为'*'
),如果没排查过,再看该坐标在雷的棋盘mine
中是否是雷,如果是雷则游戏结束,显示雷的棋盘并提示被炸死;如果不是雷,则将找到非雷的个数win
加 1,计算该坐标周围雷的数量并更新显示棋盘show
中对应位置为相应的数字字符,然后重新显示棋盘,循环继续,直到找到的非雷个数达到总格子数减去雷的数量(即所有非雷格子都被排查完),此时游戏胜利,显示雷的棋盘并提示排雷成功。
cpp
#include"game.h"
//初始化棋盘
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;
printf("------扫雷游戏------\n");
for (j = 0;j <= col;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");
}
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 get_mine_count(char board[ROWS][COLS], int x, int y)
{
// 通过将周围坐标对应的字符值('0'或'1')转换为数字(减去字符'0'的ASCII值)并求和,来统计雷的数量
// 例如字符'1'减去'0'得到数字1,代表有雷,然后把8个方向的值累加起来
return (board[x - 1][y] + board[x - 1][y - 1] + board[x][y - 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] + board[x][y + 1] + board[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("请输入要排查的坐标:>");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] != '*')
{
printf("该坐标被排查过了,不能重复排查\n");
continue;
}
else
{
//如果是雷
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, ROW, COL);
break;
}
else//如果不是雷
{
win++;
//统计mine数组中x,y坐标周围有几个雷
int count = get_mine_count(mine, x, y);
show[x][y] = count + '0';//转换成数字字符
DisplayBoard(show, ROW, COL);
}
}
}
else
{
printf("输入的坐标非法,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
3.源文件(test.c)部分
(menu
函数)
这个函数很简单,就是在控制台打印出游戏的菜单,显示了两个选项,分别是1.play
用于开始游戏和0.exit
用于退出游戏。
(game
函数)
game
函数实现了一局游戏的完整流程。首先定义了两个二维字符数组,mine
数组用于存放雷的信息(初始化为全'0'
),show
数组用于存放展示给玩家看到的棋盘信息(初始化为全'*'
),接着调用InitBoard
函数初始化这两个数组,然后通过SetMine
函数在mine
数组中布置雷,再用DisplayBoard
函数显示初始的玩家棋盘(全是'*'
的样子),最后调用FindMine
函数开始排查雷,进入游戏的交互环节。
(main
函数)
main
函数是整个程序的入口点。首先初始化随机数生成器(通过调用srand
函数并传入当前时间作为种子,确保每次游戏雷的布置是随机的),然后进入一个do-while
循环,在循环中先调用menu
函数显示游戏菜单,接着让用户输入选择,根据用户输入的不同选项(通过switch
语句判断),如果输入1
则调用game
函数开始一局游戏,如果输入0
则退出游戏,其他输入则提示选择错误,循环继续,直到用户选择退出游戏为止,最后返回0
表示程序正常结束。
cpp
#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 };//存放排查到雷的信息
//初始化数组内容为指定内容
//mine 数组在没有布置雷的时候,都是'0'
InitBoard(mine, ROWS, COLS,'0');
//show 数组在没有排查到雷的时候,都是'*'
InitBoard(show, ROWS, COLS,'*');
//设置雷
SetMine(mine,ROW,COL);
//显示初始棋盘(给玩家看的)
DisplayBoard(show, ROW, COL);
//DisplayBoard(mine, ROW, COL);
//排查雷
FindMine(mine,show,ROW,COL);
// 游戏结束后询问是否再来一局
char choice;
printf("是否再来一局?(y/n)");
scanf(" %c", &choice); // 注意前面的空格,用于跳过输入缓冲区可能存在的换行符等空白字符
if (choice == 'y' || choice == 'Y')
{
game();
}
}
int main()
{
int input = 0;
//设置随机数的生成起点
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}