今天用C语言写一个扫雷的小游戏,想必大家都玩过,就是输入一个坐标后这个位置周围一圈有多少雷都会显示出来,如果没有雷的话,就会向四周展开雷盘,当然也可以标记雷。当被炸死或者只留下雷时游戏就会结束。网页版游戏内的截图大致是这样的:
为了代码的可读性以及方便管理,我们创建三个文件,test.c \ add.c \ add.h。test.c文件中放主函数和一些最基本的函数,add.c文件中放一些自己写的实现部分功能的函数,add.h文件中放头文件和函数声明。这样其它两个文件就只需包含add.h文件即可。
因为游戏肯定可以玩很多局,所以还是用do while循环,里边根据玩家的选择执行不同的语句,所以用switch case语句。为了界面整洁,还使用了清屏指令cls。
主函数的代码是这样的
int main() {
srand((unsigned int) time(NULL));
int input = 0;
do{
menu();
printf("请选择->:");
scanf("%d", &input);
system("cls");
switch (input) {
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入->:\n");
break;
}
} while (input);
return 0;
}
menu函数其实就是供玩家选择的,
void menu() {
printf("********************\n");
printf("******1.play********\n");
printf("******0.exit********\n");
printf("********************\n");
}
下面是最复杂的game函数,我们要想一个逻辑,我们要先用二维数组去创建雷盘,创建几个呢?两个,一个用于存放雷的位置,有雷的位置放‘1’,没雷的位置放‘0’;另一个用于呈现给玩家,未知的地方用*以表未知,玩家排查出来的位置放上周围雷的个数。然后是给雷进行初始化,把二维数组的各个位置放上*或者是‘0’,之后就是打印雷盘,然后需要设置雷,最后是排查雷。大致是这样的
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(show, ROW, COL);//打印雷盘
Findmine(mine, show, ROW, COL);//排查雷
}
mine数组是设置雷的数组,show数组是打印给玩家看的。这里的雷盘的行数和列数可以用define定义一下,在add.h文件中可以去定义。并且还有一个问题就是设置的雷的行数列数要比实际玩的多上下左右各一排(列),因为只有这样我们在数周围雷的个数时才不会越界。
show数组上就全初始化为*,mine数组就全部初始化为‘0’。
void Initboard(char board[ROWS][COLS], int rows, int cols, char ret)
{
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
board[i][j] = ret;
}
}
}
打印show数组是比较简单的,打印时最好加上行号和和列号看起来是比较方便的。
void Displayboard(char board[ROWS][COLS], int row, int col) {
printf("---------扫雷--------------\n");
for (int i = 0; i <= col; 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");
}
printf("---------扫雷--------------\n");
}
设置雷的话肯定是随机设置,这里就又用到了我们的rand函数,如果你不会可以去看看我之前博客有详细讲rand函数。雷的数量我们也用define定义一下,也是为了我们方便调试和方便更改难度。
void Setmine(char board[ROWS][COLS], int row, int col) {
int count = EASY_COUNT;
while (count) {
int x = rand() % ROW + 1;//这样可以产生1~9的随机数字
int y = rand() % COL + 1;
if (board[x][y] != '1') {
board[x][y] = '1';
count--;
}
}
}
这个函数就是让玩家输入坐标,去显示这个位置是不是雷,以及周围有多少雷,计算周围雷的个数也要创建一个函数
int find_num(char mine[ROWS][COLS], int x, int y) {
return 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] + mine[x - 1][y] - 8 * '0';
}
并且玩家也应可以标记雷,也设置了一个函数
void Markmine(char show[ROWS][COLS], int row, int col) {
while (1) {
int x = 0;
int y = 0;
printf("请输入想标记的坐标->:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) {
if (show[x][y] == '*') {
show[x][y] = '!';
break;
}
else {
printf("该坐标不能被标记,请重新输入\n");
}
}
else {
printf("输入非法,请重新输入\n");
}
}
}
标记后会变成‘!’,因为如果这个坐标周围没有雷的话应该爆炸式展开,这用递归的方式也创建一个函数
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* pw) {
if (x >= 1 && x <= ROW && y >= 1 && y <= COL) {
int num = 0;
num = find_num(mine, x, y);
if (num == 0) {
(*pw)--;
show[x][y] = ' ';
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; i++) {
for (j = y - 1; j <= y + 1; j++) {
if (show[i][j] == '*')
expand(mine, show, i, j, pw);
}
}
}
else {
(*pw)--;
show[x][y] = num + '0';
}
}
}
有了这些函数,就可以实现排查雷的功能
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
int count = ROW * COL - EASY_COUNT;
int* pw = &count;
while (count) {
char arr[10] = { 0 };
gets(arr);
printf("请输入要排查的坐标->:");
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL && show[x][y] == '*') {
if (mine[x][y] == '1') {
system("cls");
printf("很抱歉,你被炸死了\n");
Displayboard(mine, ROW, COL);
break;
}
else {
expand(mine, show, x, y, pw);
system("cls");
Displayboard(show, ROW, COL);
char ch = 0;
while ((ch = getchar()) != '\n');
printf("如果需要标记雷的位置,请按Y\\y,否则请按任意键继续->:");
scanf("%c", &ch);
if (ch == 'Y' || ch == 'y') {
Markmine(show, ROWS, COLS);
system("cls");
Displayboard(show, ROW, COL);
}
}
}
else if (x >= 1 && x <= ROW && y >= 1 && y <= COL && show[x][y] != '*')
printf("重复输入,请重新输入\n");
else {
printf("输入错误,请重新输入->:\n");
}
}
if (count == 0) {
system("cls");
printf("恭喜你,排出所有雷\n");
Displayboard(mine, ROW, COL);
}
}
当被炸死或者只剩雷时就会游戏结束,胜利或失败。被炸死的条件好判断,排除完了怎么判断呢?就是记录一下非雷的个数,每标记一个雷就减一就可以了。那么这个游戏的基本实现就是这样了。下面呈上游戏的所有代码。
#include"add.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 };
Initboard(mine, ROWS, COLS, '0');//初始化
Initboard(show, ROWS, COLS, '*');
Setmine(mine, ROW, COL);//设置雷
Displayboard(show, ROW, COL);//打印雷盘
Findmine(mine, show, ROW, COL);//排查雷
}
int main() {
srand((unsigned int) time(NULL));
int input = 0;
do{
menu();
printf("请选择->:");
scanf("%d", &input);
system("cls");
switch (input) {
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入->:\n");
break;
}
} while (input);
return 0;
}
#include"add.h"
void Initboard(char board[ROWS][COLS], int rows, int cols, char ret)
{
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
board[i][j] = ret;
}
}
}
void Displayboard(char board[ROWS][COLS], int row, int col) {
printf("---------扫雷--------------\n");
for (int i = 0; i <= col; 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");
}
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] != '1') {
board[x][y] = '1';
count--;
}
}
}
int find_num(char mine[ROWS][COLS], int x, int y) {
return 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] + mine[x - 1][y] - 8 * '0';
}
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* pw) {
if (x >= 1 && x <= ROW && y >= 1 && y <= COL) {
int num = 0;
num = find_num(mine, x, y);
if (num == 0) {
(*pw)--;
show[x][y] = ' ';
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; i++) {
for (j = y - 1; j <= y + 1; j++) {
if (show[i][j] == '*')
expand(mine, show, i, j, pw);
}
}
}
else {
(*pw)--;
show[x][y] = num + '0';
}
}
}
void Markmine(char show[ROWS][COLS], int row, int col) {
while (1) {
int x = 0;
int y = 0;
printf("请输入想标记的坐标->:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) {
if (show[x][y] == '*') {
show[x][y] = '!';
break;
}
else {
printf("该坐标不能被标记,请重新输入\n");
}
}
else {
printf("输入非法,请重新输入\n");
}
}
}
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
int count = ROW * COL - EASY_COUNT;
int* pw = &count;
while (count) {
char arr[10] = { 0 };
gets(arr);
printf("请输入要排查的坐标->:");
int x = 0;
int y = 0;
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL && show[x][y] == '*') {
if (mine[x][y] == '1') {
system("cls");
printf("很抱歉,你被炸死了\n");
Displayboard(mine, ROW, COL);
break;
}
else {
expand(mine, show, x, y, pw);
system("cls");
Displayboard(show, ROW, COL);
char ch = 0;
while ((ch = getchar()) != '\n');
printf("如果需要标记雷的位置,请按Y\\y,否则请按任意键继续->:");
scanf("%c", &ch);
if (ch == 'Y' || ch == 'y') {
Markmine(show, ROWS, COLS);
system("cls");
Displayboard(show, ROW, COL);
}
}
}
else if (x >= 1 && x <= ROW && y >= 1 && y <= COL && show[x][y] != '*')
printf("重复输入,请重新输入\n");
else {
printf("输入错误,请重新输入->:\n");
}
}
if (count == 0) {
system("cls");
printf("恭喜你,排出所有雷\n");
Displayboard(mine, ROW, COL);
}
}
#pragma once
#include
#include
#include
#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 ret);
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);