“少年气,是历尽千帆举重若轻的沉淀,也是乐观淡然笑对生活的豁达!” 今天我们学习一下扫雷游戏怎么用C语言来实现!
一个扫雷盘面由许多方格(cell)组成,方格中随机分布着一定数量的雷(mine),一个格子中至多只有1雷。胜利条件是打开所有安全格(非雷格,safe cell),失败条件是打开了一个雷格(踩雷)。下面图片中是一个9*9的示例:
和前面的三子棋一样,这里,我们也需要三个源文件来共同实现这个程序。
(1)头文件
game.h
,头文件里是用来存放函数的声明,#define常量的定义,库函数的引用的。
(2)源文件test.c
,这个文件里面放的是游戏的测试逻辑。
(3)源文件game.c
,这个文件里面放的是游戏的实现逻辑(函数实现)。
这里呢,我们和三子棋一样,还是通过switch语句给用户选择,当用户输入不同的数字,我们的程序就会给出不同的功能。
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
#include
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;
}
这里我们简化游戏,设置一个
9*9
的棋盘,安置10个雷
。
排查过程如下:
1.如果这个位置是雷,那么游戏结束。
2.如果把不是雷的位置都找出来了,那么游戏结束。
3.如果这个位置不是雷,就计算这个位置的周围的8个格子有几个雷,并显示出雷的个数。
我们这里的棋盘是9*9
的,可以和三子棋一样,先将每个棋盘都初始化为0
,如下图所示,然后有雷的地方填上1
,如下图所示:
但是,我们从游戏规则中知道,当我们点到一个不是雷的格子的时候,要返回它周围八个格子中雷的个数。如下图中,如果我们点到了绿色1
的那个格子,那么该位置将返回1
这个值,此时,这里的1
就会和表示雷的1
就混起来了。
同时,当我们点到一个处于四边的格子的时候,还会出现越界的问题:
这个时候,我们就可以考虑在排查雷的时候,将棋盘扩展成11*11
的棋盘。
同时,我们可以将排查雷的9*9
的格子里都初始化为*
,避免出现两个1意义不同混淆的情况。这样的话,我们就得到两个11*11
的棋盘。
//game.h
#pragma once
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘函数的声明
void Init_Board(char board[ROWS][COLS], int rows, int cols, char set);
//game.h*
//初始化棋盘的定义
#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;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//test.c
void game()
{
char assign[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
Init_Board(assign,ROWS,COLS,'0');
Init_Board(show, ROWS, COLS, '*');
}
初始化完成之后,我们想要验证一下对不对呢?这个时候,我们就需要将棋盘打印出来。
虽然我们这里初始化的是11*11
的棋盘,但是用户需要的是中间区域的9*9
,因此,我们只需要打印中间的9*9
就可以了。
//game.h
//打印棋盘函数的声明
void Display_Board(char board[ROWS][COLS], int row, int col);
//game.c
//打印棋盘函数的定义
//我们只需要打印出中间的9*9的格子
void Display_Board(char board[ROWS][COLS], int row, int col)
{
int i = 0;
for (i = 1; i <= row; i++)
{
int j = 0;
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
//test.c
void game()
{
Display_Board(assign, ROW, COL);
}
这里我们需要把行号和列号也打印出来,能够看的更清晰。代码进行这样的修改就好啦!
再优化一下,就如下图所示:
我们希望的是在这个9*9的棋盘里随机生成10个雷,这里我们就想到了能够产生随机数的函数rand()和srand()
,使用这两个函数,需要添加头文件#include
,同时,在主函数内还需要添加语句srand((unsigned int)time(NULL));
来产生随机数的种子。
//game.h
#define Easy_Thunder 10
#include
#include
//布置雷函数的声明
void Set_thunder(char board[ROWS][COLS], int row, int col);
//game.c
//布置雷函数的定义
void Set_thunder(char board[ROWS][COLS], int row, int col)
{
int count = Easy_Thunder;
while (count)
{
int x = rand() % row + 1;//生成横坐标
int y = rand() % col + 1;//生成纵坐标
if (board[x][y] == '0')//防止在同一个地方重复布雷
{
board[x][y] = '1';
count--;
}
}
}
//test.c
void game()
{
char assign[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
Init_Board(assign,ROWS,COLS,'0');
Init_Board(show, ROWS, COLS, '*');
Display_Board(assign, ROW, COL);
Set_thunder(assign, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
return 0;
}
那么,代码写好了,我们来验证一下,这样是不是能够布置出十个雷呢?
我们开始随机点一个格子,当这个格子不是雷的时候,可以计算它周围八个格子字符的ASCII码值
,减去8个'0'
的ASCII码值,就可以知道,这个格子周围有多少个雷了,然后我们加上'0'的ASCII码值
就能在该处的格子上返回相应的字符。当我们把所有不是雷的格子找出来的时候,给用户提示排雷成功。当我们踩到雷的时候,也给出相应的提示给用户,游戏结束。这里和三子棋一样,我们可以添加清屏的操作system("cls");
,让我们的游戏面板不是那么的冗长。
//game.h
//排查雷函数的声明
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//game.c
int Getassign(char assign[ROWS][COLS], int row, int col)//查找不是雷的格子周围有几个雷
{
return (assign[row - 1][col - 1]//字符'0'的ASCII值为48,字符'1'的ASCII值为49
+ assign[row - 1][col]
+ assign[row - 1][col + 1]
+ assign[row][col - 1]
+ assign[row][col + 1]
+ assign[row + 1][col - 1]
+ assign[row + 1][col]
+ assign[row + 1][col + 1] - 8 * '0');
}
//排查雷函数的定义
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//计算没有踩到雷的次数
while (win<col*row-Easy_Thunder)
{
printf("请输入坐标:>");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (assign[x][y] == '1')
{
printf("很遗憾,你被炸死了!\n");
Display_Board(assign, ROW, COL);
break;
}
else
{
//如果这个位置不是雷就统计周围八个格子雷的个数
int c = Getassign(assign, x, y);
show[x][y] = c + '0';
Display_Board(show, ROW, COL);
system("cls");
Display_Board(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
if (win == row * col - Easy_Thunder)
{
printf("恭喜你排雷成功!\n");
Display_Board(assign, ROW, COL);
}
}
//test.c
void game()
{
Find_thunder(assign, show, ROW, COL);//排雷函数的引用
}
这里,我们随机输入,不用思考,试一下踩到雷的结果:
那么,如果我们仔细思考,看看当我们把所有不是雷的格子找出来的时候,能不能成功:
//game.h
#pragma once
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define Easy_Thunder 10
#include
#include
//初始化棋盘函数的声明
void Init_Board(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘函数的声明
void Display_Board(char board[ROWS][COLS], int row, int col);
//布置雷函数的声明
void Set_thunder(char board[ROWS][COLS], int row, int col);
//排查雷函数的声明
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//game.c
#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;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//打印棋盘函数的定义
//我们只需要打印出中间的9*9的格子
void Display_Board(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("--------扫雷--------\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
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_thunder(char board[ROWS][COLS], int row, int col)
{
int count = Easy_Thunder;//布置10个雷
while (count)
{
int x = rand() % row + 1;//生成横坐标
int y = rand() % col + 1;//生成纵坐标
if (board[x][y] == '0')//防止在同一个地方重复布雷
{
board[x][y] = '1';
count--;
}
}
}
int Getassign(char assign[ROWS][COLS], int row, int col)//查找不是雷的格子周围有几个雷
{
return (assign[row - 1][col - 1]//字符'0'的ASCII值为48,字符'1'的ASCII值为49
+ assign[row - 1][col]
+ assign[row - 1][col + 1]
+ assign[row][col - 1]
+ assign[row][col + 1]
+ assign[row + 1][col - 1]
+ assign[row + 1][col]
+ assign[row + 1][col + 1] - 8 * '0');
}
//排查雷函数的定义
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//计算没有踩到雷的次数
while (win<col*row-Easy_Thunder)
{
printf("请输入坐标:>");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (assign[x][y] == '1')
{
printf("很遗憾,你被炸死了!\n");
Display_Board(assign, ROW, COL);
break;
}
else
{
//如果这个位置不是雷就统计周围八个格子雷的个数
int c = Getassign(assign, x, y);
show[x][y] = c + '0';
Display_Board(show, ROW, COL);
system("cls");//清屏操作
Display_Board(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
if (win == row * col - Easy_Thunder)
{
printf("恭喜你排雷成功!\n");
Display_Board(assign, ROW, COL);
}
}
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
#include
void menu()
{
printf("******************************\n");
printf("********* 1.play *********\n");
printf("********* 0.exit *********\n");
printf("******************************\n");
}
void game()
{
char assign[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
Init_Board(assign,ROWS,COLS,'0');
Init_Board(show, ROWS, COLS, '*');
Display_Board(assign, ROW, COL);
Set_thunder(assign, ROW, COL);
Find_thunder(assign, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
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;
}
以上就是关于扫雷游戏的全部代码啦!当然这个程序还是存在可优化的空间(比如我们的游戏只能一个一个点,但是不能像网页版的那样能展示一片,这还有待思考),欢迎大家在评论区交流,优化代码。