当前位置:网站首页>C语言实现扫雷游戏(完整版)
C语言实现扫雷游戏(完整版)
2022-07-06 09:19:00 【是王久久阿】
和三子棋一样,扫雷的核心思想也是二维数组的利用。
扫雷游戏还用到:循环语句、分支语句、函数的递归以及指针等知识,具体知识在前几篇文章中有过讲解。
利用C语言在命令窗口实现扫雷游戏:
1、输入方式:玩家输入坐标,如果不是雷,游戏继续;如果是雷,游戏结束。
2、坐标的判定:如果该坐标周边有雷,则显示周围雷的信息;如果该坐标周围没有雷,则一次展开多项,直到展开到雷的附近。
3、游戏结束判定:当玩家触碰到雷,或者棋盘上所有非雷的区域被玩家全部找到。
目录
主菜单
主菜单用do while循环结构,实现玩完一次游戏不过瘾,还可以接着玩。(do while 循环可以保证整个循环至少运行一次)
//text.c
#include "game.h"
void menu()//菜单
{
printf("********************\n");
printf("***** 1.paly *****\n");
printf("***** 0.exit *****\n");
printf("********************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择(1/0):>\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}
为了之后测试可以精确找到bug位置所在,我们要养成每写完一部分代码,就测试一下的良好习惯。这里我们测试一下主菜单部分的代码是否可以正常运行。
游戏部分
1.设置棋盘及初始化
(一)设置棋盘
棋盘的规划:
mine棋盘:存储布雷的信息(不给玩家显示)
show棋盘:玩家游戏的棋盘。(给玩家显示)
棋盘的行和列:
1、通过#define定义常量的方式来设定,方便以后代码的更新。
2、为了之后之后访问二维数组时不越界,设置棋盘时,行和列都要+2。(如果想玩9×9的棋盘,那么在最初设置棋盘时要设置成11×11)
//game.h
//设置棋盘的行和列
#define ROW 11
#define COL 11
//text.c
void game()
{
//将布雷的信息存储到mine中
char mine[ROW][COL] = { 0 };
//将显示的信息存储到show中
char show[ROW][COL] = { 0 };
}
(二)初始化
Initboard:利用for循环遍历二维数组的每一个元素,将棋盘初始化。
初始化mine棋盘:布雷的棋盘初始化全为’0‘。(这里的0为字符0)
初始化show棋盘:显示的棋盘初始化全为’*‘。
//game.h
//初始化
void Initboard(char board[ROW][COL], int row, int col, char set);
//game.c
//初始化棋盘
void Initboard(char board[ROW][COL], int row,int col,char set)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = set;
}
}
}
//text.c
void game()
{
//将布雷的信息存储到mine中
char mine[ROW][COL] = { 0 };
//将显示的信息存储到show中
char show[ROW][COL] = { 0 };
//布雷的棋盘初始化全为0
Initboard(mine, ROW, COL, '0');
//显示的棋盘初始化全为*
Initboard(show, ROW, COL, '*');
}
定义完棋盘后,进入调试模式,通过监视窗口来监视我们的两副棋盘是否正确。
2.布雷
雷的数量也是用define定义,方便之后的修改。
Setting:通过rand随机生成雷的坐标,每次生成坐标时需要判断该坐标是否合法。并且注意,坐标的范围是棋盘游戏的范围,而不是最初设定时的范围。
(若要使用rand函数,需要在主函数中对rand函数进行修饰:srand((unsigned int)time(NULL));,否则每次生成的随机值将一样)
//game.h
//布雷和打印的行、列
#define ROWS ROW-2
#define COLS COL-2
//布雷的数量
#define Easy 10
//game.c
//布雷
void Setting(char board[ROW][COL], int rows, int cols)
{
int count = Easy;
while (count)
{
int x = rand() % rows + 1;
int y = rand() % rows + 1;
if (board[x][y] != 1)
{
board[x][y] = '1';
count--;
}
}
}
//text.c
void game()
{
//将布雷的信息存储到mine中
char mine[ROW][COL] = { 0 };
//将显示的信息存储到show中
char show[ROW][COL] = { 0 };
//布雷的棋盘初始化全为0
Initboard(mine, ROW, COL, '0');
//显示的棋盘初始化全为*
Initboard(show, ROW, COL, '*');
//布雷
Setting(mine, ROWS, COLS);
}
//main函数
srand((unsigned int)time(NULL));
布置完雷后,也是进入调试模式来检查代码运行是否正确。
3.打印棋盘
Dispaly:只需要打印show棋盘即可,定义这里打印棋盘与初始化的方法一致,都是用for循环遍历每个元素。唯一不同的地方在于为了方便输入坐标,这里将行号和列号在第一行和第一列打印。(打印完行号后需要换行,打印列号则不用)
//game.h
//打印棋盘
void Display(char board[ROW][COL], int rows, int cols);
//text.c
//打印棋盘
void Display(char board[ROW][COL], int rows, int cols)
{
int i = 0;
int j = 0;
for (i = 0; i <= rows; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= rows; i++)
{
printf("%d ", i);
for (j = 1; j <= cols; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("\n");
}
4.Cue提示雷的信息
在扫雷游戏中,如果该坐标不是雷,并且周围一圈有雷时,会提示周围雷的个数。
Cue:将玩家输入的x、y参数传给Cue函数,用for循环遍历周围一圈的坐标,用临时变量count来记录周围雷的个数。将布设的雷的坐标设定为’1‘。
//提示
int Cue(char mine[ROW][COL], int x, int y)
{
int i = 0;;
int j = 0;
int count = 0;
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
if (mine[x + i][y + j] == '1')
{
count++;
}
}
}
return count;
}
5.Unfold展开功能
在扫雷游戏中,如果该坐标周边有雷,则显示周围雷的信息;如果该坐标周围没有雷,则一次展开多项,直到展开到雷的附近。
这里主要利用Unfold函数的递归,配合定义的Cue函数来实现。
递归需要满足以下两点:
1、设定递归结束条件。
- 坐标超限
- 该坐标是雷
- 该坐标已被查找
- 该坐标已标记雷的信息
2、已查找过的坐标不可重复查找,否则将会陷入死递归。
- mine[x][y] = '2'
- 将查找过的坐标标记为非0,非1的数字即可
此外,还需要在函数外部定义count,count为棋子的总数-布雷的数量,用来评判游戏结束。
外部传count参数时需要进行取地址,内部函数递归时,count已经是地址了,不要再取地址了。
//多个展开
void Unfold(char mine[ROW][COL], char show[ROW][COL], int x, int y,int* count)
{
int i = 0;
int j = 0;
//终结标志:
//1、坐标超限
//2、该坐标是雷
//3、该坐标已被查找
if (1 <= x && x <= 9 && 1 <= y && y <= 9 && mine[x][y] == '0' && show[x][y] == '*')
{
mine[x][y] = '2';//查找过的坐标在mine里进行标记
if (Cue(mine, x, y))//周围有雷提示雷的数量
{
show[x][y] = '0' + Cue(mine, x, y);//将整数转换成字符
(*count)--;
}
else
{
show[x][y] = ' ';//没有雷就显示空格
(*count)--;
for (i = -1; i <= 1; i++)//针对(x,y)周围八个格子进行查找
{
for (j = -1; j <= 1; j++)
{
Unfold(mine, show, x + i, y + j,count);//进行递归,这里的count已经是地址了,不要再取地址了
}
}
}
}
}
6.Guess排查
在玩家输入坐标后,需要对该坐标进行判断:
- 坐标非法,重新输入
- 坐标合法,进入Unfold函数,检查是否展开,并提示雷的信息
- 坐标是炸弹,游戏结束
这里循环的依据就是count,当count为0时,跳出循环,宣布游戏结果。
//game.h
//排查
void Guess(char mine[ROW][COL], char show[ROW][COL], int rows, int cols);
//game.c
//排查
void Guess(char mine[ROW][COL], char show[ROW][COL], int rows, int cols)
{
int x = 0;
int y = 0;
//记录排查次数
int count = rows * cols - Easy;
while (count)
{
scanf("%d %d", &x, &y);
//判断坐标是否正确
//1、坐标在范围内
//2、坐标没被使用
if (1 <= x && x <= 9 && 1 <= y && y <= 9 && show[x][y] == '*')
{
if (mine[x][y] != '1')
{
printf("Continue......\n");//不是雷就继续
show[x][y] = ' ';
Display(show, ROWS, COLS);
count--;
}
else
{
printf("很遗憾,再接再厉\n");
DisplayEnd(mine,show, ROWS, COLS);//将布雷的情况打印出来。
break;
}
}
else
printf("坐标非法,请重新输入\n");
}
if (count == 0)
{
printf("恭喜你\n");
Display(show, ROWS, COLS);
}
}
在游戏结束后,还需要将游戏的结果及布雷的情况打印出来,这里不单单只是打印mine棋盘,需要将mine和show棋盘结合一下。
//打印结局
void DisplayEnd(char mine[ROW][COL], char show[ROW][COL], int rows, int cols)
{
{
int i = 0;
int j = 0;
for (i = 0; i <= rows; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= rows; i++)
{
printf("%d ", i);
for (j = 1; j <= cols; j++)
{
if (mine[i][j] == '1')//如果是雷就打印出来
{
printf("%c ",mine[i][j]);//与打印棋盘唯一不同之处
}
else
{
printf("%c ", show[i][j]);
}
}
printf("\n");
}
printf("\n");
}
}
试玩
看来运气并不好,没有展开一片区域,换个坐标试试。
尝试输入错误的坐标,检查系统是否能检测出来。
(4,5)明显是个雷,让我们看一下结果是否如我们预料到那样。
通过更改define定义的常量,来更改游戏的难度。当然也可以把扫雷做成界面的形式,具体的UI设计也非常简单,这里只讲基础的算法知识,UI的设计可以自行学习。
整体代码
整体代码如下,当然还有很多值得优化的地方,大家有任何想法可以互相交流。
(一)game.h
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//设置棋盘的行、列
#define ROW 11
#define COL 11
//布雷和打印的行、列
#define ROWS ROW-2
#define COLS COL-2
//布雷的数量
#define Easy 10
//初始化
void Initboard(char board[ROW][COL], int row, int col, char set);
//布雷
void Setting(char board[ROW][COL], int rows, int cols);
//打印棋盘
void Display(char board[ROW][COL], int rows, int cols);
//打印结局
void DisplayEnd(char mine[ROW][COL], char show[ROW][COL], int rows, int cols);
//排查
void Guess(char mine[ROW][COL], char show[ROW][COL], int rows, int cols);
(二)game.c
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
//初始化棋盘
void Initboard(char board[ROW][COL], int row, int col, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = set;
}
}
}
//布雷
void Setting(char board[ROW][COL], int rows, int cols)
{
int count = Easy;
while (count)
{
int x = rand() % rows + 1;
int y = rand() % rows + 1;
if (board[x][y] != 1)
{
board[x][y] = '1';
count--;
}
}
}
//打印棋盘
void Display(char board[ROW][COL], int rows, int cols)
{
int i = 0;
int j = 0;
for (i = 0; i <= rows; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= rows; i++)
{
printf("%d ", i);
for (j = 1; j <= cols; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("\n");
}
//打印结局
void DisplayEnd(char mine[ROW][COL], char show[ROW][COL], int rows, int cols)
{
{
int i = 0;
int j = 0;
for (i = 0; i <= rows; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= rows; i++)
{
printf("%d ", i);
for (j = 1; j <= cols; j++)
{
if (mine[i][j] == '1')
{
printf("@ ");
}
else
{
printf("%c ", show[i][j]);
}
}
printf("\n");
}
printf("\n");
}
}
//提示周围雷的信息
int Cue(char mine[ROW][COL], int x, int y)
{
int i = 0;;
int j = 0;
int count = 0;
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
if (mine[x + i][y + j] == '1')
{
count++;
}
}
}
return count;
}
//多个展开
void Unfold(char mine[ROW][COL], char show[ROW][COL], int x, int y,int* count)
{
int i = 0;
int j = 0;
//终结标志:
//1、坐标超限
//2、该坐标是雷
//3、该坐标已被查找
if (1 <= x && x <= 9 && 1 <= y && y <= 9 && mine[x][y] == '0' && show[x][y] == '*')
{
mine[x][y] = '2';//查找过的坐标在mine里进行标记
if (Cue(mine, x, y))//周围有雷提示雷的数量
{
show[x][y] = '0' + Cue(mine, x, y);//将整数转换成字符
(*count)--;
}
else
{
show[x][y] = ' ';//没有雷就显示空格
(*count)--;
for (i = -1; i <= 1; i++)//针对(x,y)周围八个格子进行查找
{
for (j = -1; j <= 1; j++)
{
Unfold(mine, show, x + i, y + j,count);//进行递归,这里的count已经是地址了,不要再取地址了
}
}
}
}
}
//排查
void Guess(char mine[ROW][COL], char show[ROW][COL], int rows, int cols)
{
int x = 0;
int y = 0;
//记录排查次数
int count = rows * cols - Easy;
while (count)
{
printf("请输入坐标:>\n");
scanf("%d %d", &x, &y);
//判断坐标是否正确
//1、坐标在范围内
//2、坐标没被使用
if (1 <= x && x <= 9 && 1 <= y && y <= 9 && show[x][y] == '*')
{
if (mine[x][y] == '1')
{
printf("很遗憾,再接再厉\n");
DisplayEnd(mine, show, ROWS, COLS);//将布雷的情况打印出来。
break;
}
else
{
//不是雷就继续
Unfold(mine, show, x, y,&count);
Display(show, ROWS, COLS);
printf("Continue......\n");
}
}
else
printf("坐标非法,请重新输入\n");
}
if (count == 0)
{
printf("恭喜你\n");
Display(show, ROWS, COLS);
}
}
(三)text.c
#define _CRT_SECURE_NO_WARNINGS
//扫雷
#include "game.h"
void menu()
{
printf("********************\n");
printf("***** 1.paly *****\n");
printf("***** 0.exit *****\n");
printf("********************\n");
}
void game()
{
//将布雷的信息存储到mine中
char mine[ROW][COL] = { 0 };
//将显示的信息存储到show中
char show[ROW][COL] = { 0 };
//布雷的棋盘初始化全为0
Initboard(mine, ROW, COL, '0');
//显示的棋盘初始化全为*
Initboard(show, ROW, COL, '*');
//布雷
Setting(mine, ROWS, COLS);
//打印棋盘
Display(show, ROWS, COLS);
//排查
Guess(mine, show, ROWS, COLS);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择(1/0):>\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}
边栏推荐
- Tyut Taiyuan University of technology 2022 introduction to software engineering summary
- Share a website to improve your Aesthetics
- Questions and answers of "signal and system" in the first semester of the 22nd academic year of Xi'an University of Electronic Science and technology
- Introduction and use of redis
- Record: Navicat premium can't connect to MySQL for the first time
- Arduino+ water level sensor +led display + buzzer alarm
- List set map queue deque stack
- E-R graph to relational model of the 2022 database of tyut Taiyuan University of Technology
- 面试必备:聊聊分布式锁的多种实现!
- MySQL Database Constraints
猜你喜欢
121道分布式面试题和答案
Record: the solution of MySQL denial of access when CMD starts for the first time
抽象类和接口
RTKLIB: demo5 b34f. 1 vs b33
Small exercise of library management system
121 distributed interview questions and answers
阿里云微服务(二) 分布式服务配置中心以及Nacos的使用场景及实现介绍
Application architecture of large live broadcast platform
MySQL 三万字精华总结 + 面试100 问,吊打面试官绰绰有余(收藏系列
Role movement in the first person perspective
随机推荐
架构师怎样绘制系统架构蓝图?
[while your roommate plays games, let's see a problem]
Questions and answers of "basic experiment" in the first semester of the 22nd academic year of Xi'an University of Electronic Science and technology
(super detailed II) detailed visualization of onenet data, how to plot with intercepted data flow
Design a key value cache to save the results of the most recent Web server queries
Tyut Taiyuan University of technology 2022 introduction to software engineering summary
Alibaba cloud side: underlying details in concurrent scenarios - pseudo sharing
MySQL backup -- common errors in xtrabackup backup
Error: symbol not found
最新坦克大战2022-全程开发笔记-2
Novatel board oem617d configuration step record
最新坦克大战2022-全程开发笔记-3
CorelDRAW plug-in -- GMS plug-in development -- Introduction to VBA -- GMS plug-in installation -- Security -- macro Manager -- CDR plug-in (I)
IPv6 experiment
Rich Shenzhen people and renting Shenzhen people
MySQL 30000 word essence summary + 100 interview questions, hanging the interviewer is more than enough (Collection Series
View UI Plus 发布 1.3.0 版本,新增 Space、$ImagePreview 组件
MySQL 三万字精华总结 + 面试100 问,吊打面试官绰绰有余(收藏系列
Ten minutes to thoroughly master cache breakdown, cache penetration, cache avalanche
TYUT太原理工大学2022数据库考试题型大纲