你有过小时候在学校的计算机课总会偷偷打开电脑上的单机游戏“扫雷”,然后一玩就是一节课的经历吗?那么在学过C语言之后我们有没有办法自己做出一个“扫雷”游戏呢?答案是肯定的,接下来,教你用8步很容易得创造一个扫雷游戏。
实现思路:
首先我们要先创建一个9*9的一个棋盘用来存放雷区块和空区块的信息,在这里我们使用字符'0'表示空区块,用字符'1'表示雷区块。此时,我们很容易会想到用一个二维数组来存放这些信息。如下图:
另外,我们还需要再创建一个9*9的表格用来存放排雷的信息,也就是一个区块周围的8个区块有几个雷。假如所排除的区块旁边有2个雷,那么这个区块所显示的就是字符'2'。但是,如果所排除的区块是一个边缘的区块,那么周围所统计的就不再是8个区块了。例如如果我们所排除的区块是第9行第9个区块,那么周围就只有3个雷需要统计。如下图:
那么,怎么解决这个问题呢?我们可以再在9*9的二维数组外面再加一圈区块,这样,我们无论排除的是哪一个区块,统计周围的区块都会是8个。如下图:
这样,我们就需要作2个11*11的棋盘,一个用来存放雷,我们将其命名为mine数组,另一个用来存放雷信息的数组,我们将其命名为show数组。
思路明确后,该我们用双手来实践了(这里使用的是VS2022版本来演示)
实现步骤:
1.为了提高代码的清晰度和为了使我们的思路更加地清晰,我们采用分文件的方式来编写代码。①test.c文件,用来编写游戏的测试逻辑②game.c文件 ,用来编写游戏中函数的实现③game.h文件,用来编写游戏中所需要的数据类型和函数声明。创建好是如下界面
2.我们开始打印游戏的初始界面,包含在test.c文件里,文件里有包含3个函数,分别是menu函数,用来打印游戏的选项页面,game函数,用来执行游戏(此时我们先不在game函数里面编写代码)以及main函数,用来控制函数的进出,相当于游戏的进出的通道。详细代码如下:
#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()
{
}
int main()
{
int input = 0;
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;
}
3.我们开始编写游戏的执行代码,首先,我们在test.c文件game函数中写出函数的流程。①.先创建出2个字符类型的二维数组,目的是为了存放mine数组和show数组。②.用initboard函数将2个数组初始化,mine数组初始化的值全部为字符'0',show数组初始化的值全部为字符'*'。③.用setmine函数来打印出show函数来(这里也可以同时把mine打印出来看看效果,后期直接注释掉就可以了)。④.用getmine函数在mine数组里面随机布置几个雷。⑤.用findmine函数来进行最后的排雷游戏。(这里为了方便我们后期想要改变数字较为轻松的话,实参全部使用常量来表示,全部都在game.h文件里面定义)
void game()
{
char mine[ROWS][COLS] = { 0 };//存放布置好的雷
char show[ROWS][COLS] = { 0 };//存放排查出雷的信息
//初始化棋盘
//1.mine数组最开始全是'0'
//2.show数组最开始全是'*'
initboard(mine, ROWS, COLS, '0');
initboard(show, ROWS, COLS, '*');
//打印棋盘
displayboard(mine, ROW, COL);
displayboard(show, ROW, COL);
//1. 布置雷
setmine(mine, ROW, COL);
displayboard(mine, ROW, COL);
//2. 排查雷
findmine(mine, show, ROW, COL);
}
4.我们将这四个函数在game.c文件中编写出来,我们先来说initboard函数,对于这个函数相对简单,需要注意的是,我们在初始化2个数组时,可以多加一组字符变量set,这样可以直接了当的将两个数组区分开来,具体代码如下:
void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0;i < rows;i++)
{
int j = 0;
for (j = 0;j < cols;j++)
{
board[i][j] = set;
}
}
}
5.接着来讲displayboard函数,这个函数可以打印数组,注意点是要在每一行以及每一列都加上行号以及列号。具体代码如下:
void displayboard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf("--------扫雷游戏--------\n");
for (i = 0;i <= col;i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1;i <= row;i++)
{
int j = 0;
printf("%d ", i);
for (j = 1;j <= col;j++)
{
printf("%c ",board[i][j]);
}
printf("\n");
}
}
6.然后就是getmine函数,这个函数需要我们随机在9*9的棋盘上面布置10个雷。既然是随机的,那就需要我们引入随机数这个概念了。
C语⾔提供了⼀个函数叫 rand,需要包含⼀个头⽂件是:#include<stdlib.h>。这函数是可以⽣成随机数的rand函数的使⽤,rand函数会返回⼀个伪随机数,这个随机数的范围是在0~32767之间的。我们来用代码演示一遍。随机生成几个随机数:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
但是,当我们多次尝试过后会发现每一次生成的“随机数”都是相同的,这是因为rand函数返回的是一个伪随机数。那么就不能保证我们每一次生成的随机数是一个不同的数字。
这时,我们就需要再次引入一个srand函数。srand函数⽤来初始化随机数的⽣成器的,程序中在调⽤ rand 函数之前先调⽤ srand 函数,通过 srand 函数的参数来设置rand函数⽣成随机数的时候的“种子”,只要“种子”变化,每次⽣成的随机数序列也就变化起来了。
相信玩过《我的世界》的朋友对“种子”这个概念并不陌生,只要种子不一样,生成的地图也就不一样。那么,我们只要找到一个不断变化的数字,就可以生成不同的“种子”。
这时,我们需要引入一个time函数的概念:在C语⾔中有⼀个函数叫 time ,需要包含的一个头文件是:#include<time.h>,time 函数会返回当前的⽇历时间,其实返回的是1970年1⽉1⽇0时0分0秒到现在程序运⾏时间之间的差值,单位是秒。这段时间差通常也被叫做:时间戳,时间戳用time(NULL)来表示。让我们结合time,strand,rand来生成几个个随机数,代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
srand((unsigned int)time(NULL));
//seand中包含的是种子,种子不同生成的随机数不同
//srand中所包含的类型是unsigned int,(unsigned int)表示强制类型转换
//time(NULL)表示时间戳
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
随机数的概念在我另一篇文章也有详细的说明,叫三步实现猜数字游戏,有兴趣的朋友可以去看一下。【C语言】3步实现猜数字游戏-CSDN博客
然后,我们开始编写setmine函数,具体代码如下:
void setmine(char board[ROWS][COLS], int row, int col)
{
int count = COUNT;
while (count)
{
int i = rand() % row + 1;//范围是1-9
int j = rand() % col + 1;
if (board[i][j] == '0')
{
board[i][j] = '1';
count--;
}
}
}
7.我们开始编写扫雷游戏的进行代码findmine函数。这个函数,我们采用多个选择语句来运行,首先排除掉错误的坐标,在进行排雷。若排到的第一个就是雷,那么直接break退出本局游戏。若排到的是空区块,则这个空区块需要表明周围雷的信息,那么我们怎么统计空区块周围雷的信息呢?这时候,我们需要借助另一个函数赖统计空区块周围雷的信息。
这时候需要我们在findmine函数前面添加一个整数类型的函数getminecount函数,用来统计所排除区块周围有几个雷。循序渐进,我们首先来统计所排区块周围一个区块的信息。根据前文,我们知道,在棋盘上的“雷区”我们用的都是字符'1'来表示的,那么,如果我们用1的ASCII码值减去0的ASCII码值,就可以统计出其中一个区块雷的数量。我们设所排除雷区块的坐标是(x,y),然后依次再将周围8个区块的信息依次统计出来:
int getminecount(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');
}
最后,我们开始编写最后的findmine函数:
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 - COUNT)
{
printf("请输入你要排雷的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了");
displayboard(mine, ROW, COL);
break;
}
else
{
int count = getminecount(mine, x, y);
show[x][y] = count + '0';
displayboard(show, ROW, COL );
win++;
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
if (win == row * col - COUNT)
{
printf("恭喜你,排雷成功\n");
displayboard(mine, ROW, COL);
}
}
8.将这些全部完成之后,我们需要在game.h文件中将在game.c文件中的函数全部声明一遍。
实现结果:
让我们来看一看最后的整体代码:
game.h:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define 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 "game.h"
void initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0;i < rows;i++)
{
int j = 0;
for (j = 0;j < cols;j++)
{
board[i][j] = set;
}
}
}
void displayboard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf("--------扫雷游戏--------\n");
for (i = 0;i <= col;i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1;i <= row;i++)
{
int j = 0;
printf("%d ", i);
for (j = 1;j <= col;j++)
{
printf("%c ",board[i][j]);
}
printf("\n");
}
}
void setmine(char board[ROWS][COLS], int row, int col)
{
int count = COUNT;
while (count)
{
int i = rand() % row + 1;//范围是1-9
int j = rand() % col + 1;
if (board[i][j] == '0')
{
board[i][j] = '1';
count--;
}
}
}
int getminecount(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 - COUNT)
{
printf("请输入你要排雷的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了");
displayboard(mine, ROW, COL);
break;
}
else
{
int count = getminecount(mine, x, y);
show[x][y] = count + '0';
displayboard(show, ROW, COL );
win++;
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
if (win == row * col - COUNT)
{
printf("恭喜你,排雷成功\n");
displayboard(mine, ROW, COL);
}
}
test.c:
#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 };//存放排查出雷的信息
//初始化棋盘
//1.mine数组最开始全是'0'
//2.show数组最开始全是'*'
initboard(mine, ROWS, COLS, '0');
initboard(show, ROWS, COLS, '*');
//打印棋盘
//displayboard(mine, ROW, COL);
displayboard(show, ROW, COL);
//1. 布置雷
setmine(mine, ROW, COL);
//displayboard(mine, ROW, COL);
//2. 排查雷
findmine(mine, show, ROW, COL);
}
void test()
{
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);
}
int main()
{
test();
return 0;
}