C语言实现经典扫雷小游戏,利用递归实现棋盘展开
目录
前言
本篇文章,主要写一下<C语言程序实现扫雷小游戏>,会从一开始的结构思路出发,细分小步,一步一步将扫雷实现。
一、结构思路
参考与电脑上的扫雷小游戏,发现需要一下几步:1.需要一个棋盘 2.棋盘里面有提前布置好的雷 3.玩家走一步后,会显示出来周围一圈雷的个数 4.如果周围没有雷,将进行展开,直到有雷的地方停下,显示雷的个数 5.将雷排完,游戏结束。
为了书写方便这里使用3个文件,头文件:game.h 测试文件:test.c 游戏文件:game.c
二、具体步骤
1.在main()函数中调用test()函数
代码如下:
int main()
{
test();
return 0;
}
2.test()函数的实现
在test()函数中要实现 1.打印菜单 2.使用do...while循环使游戏不会停下来 3.调用游戏函数
游戏要进行至少一次,使用do....while循环刚刚好,然后Switch..case 语句进行选择,选择1.开始游戏,0.退出游戏 其他情况用 default 来解决
menu()函数来实现菜单的打印
srand((unsigned int)time(NULL))随机数生成器,为下面的放置雷做准备
menu()函数:
test()函数:
代码如下:
void test()//函数
{
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);
}
3.game()函数
在game()中来实现游戏的核心部分。
1.创建两个数组
1.创建两个数组,一个将雷的信息展示给玩家,另一个放置雷,但是需要注意的是两个数组的行和列并不相等,例如:我们将10颗雷,放在9*9的棋盘里面,因为检测周围雷的信息的时候是所下坐标周围的一圈,那么如果在棋盘边缘就不好检测,所以我们存放雷的信息的数组中需要在原来的基础上加上一圈,也就是11*11的一个数组,这样无论下到哪里所检测的都是周围一圈的信息。
为了方便更改,在头文件game.h中进行定义
#define ROW 3//mina数组的行
#define COL 3//mina数组的列
#define ROWS ROW+2//show数组的行
#define COLS COL+2//show数组的列代码如下:
//创建两个数组
char mina[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
2.对两个数组进行初始化
初始化数组时,show数组是展示给玩家的,初始化成“*”,mina数组是我们放置雷所用的初始化成“0”,因为要将两个数组都初始化,初始化的内容不一样,但只要将“*”“0”,放到字符str中就完美解决了!
//将mina初始化为‘0’,将show初始化为‘*’
Initializ(mina, ROWS, COLS, '0');
Initializ(show, ROWS, COLS, '*');
//对两个数组进行初始化
void Initializ(char board[ROWS][COLS], int rows, int cols, char str)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = str;
}
}
}
3.打印数组
为了方便进行扫雷,打印出行号列号和分割行
效果图:
//打印棋盘
void Disprint(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int c = 0;
for (j = 1; j <= col; j++)//打印列号
{
printf(" %d ", j);
}
printf("\n");//换行
for (c = 1; c <= col; c++)//打印上面的横杠
{
if (c == 1)
{
printf(" ");
}
printf("--- ");
}
printf("\n");//换行
for (i = 1; i <= row; i++)//打印每行的内容
{
printf("%d", i);//打印行号
for (j = 1; j <= col; j++)//打印每列的内容
{
printf(" %c", board[i][j]);
printf(" |");//打印每个字符之间的分隔竖杠
}
printf("\n");//换行
int s = 0;
printf(" ");
for (s = 1; s <= row; s++)//打印分割行
{
printf("---|");
}
printf("\n");//换行
}
printf("\n");//打印每一行之后换行
}
4.在mina数组中放置雷
我们要将雷随机的放到面数组中,这里也就用到了随机数生成器,用‘1’来表示雷,生成随机的坐标,然后判断这里是否是‘0‘,如果是就将雷放进去
//放置雷
void Place_mine(char mina[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = MINE;
while (count)
{
x = rand() % ROW + 1;
y = rand() % COL + 1;
if (mina[x][y] == '0')
{
mina[x][y] = '1';
count--;
}
}
}
5.排雷并显示周围雷的信息 (递归展开)
将雷布置好以后,下面就该进行排雷
输入一个合法的坐标后,进行判断,如果是雷那么结束游戏。如果不是雷那么判断此坐标周围 有没有雷,如果没有进行展开,直到周围有雷停止,然后显示雷的信息,并进行判断是否将雷排完了,如果此坐标周围有雷就进行显示,不展开。
周围雷的数量:
根据输入的坐标 x y,可以找到周围的8个坐标:[x][ y-1] ,[x][y+1] ,[x-1][y],[x+1][y],[x-1][y-1],[x-1][y+1],[x+1][y-1],[x+1][y+1].
将8个坐标加在一起得出来的数字,就是雷的数量,但要注意的是棋盘中的1和0是字符,根据ASCII码表可知字符数字减去字符0,就得到数字。那么可以求出周围的雷
代码如下:
//周围雷的数量
int Mine_amount(char mina[ROWS][COLS], int x, int y)
{
return mina[x][y - 1] + mina[x][y + 1] + mina[x + 1][y - 1] + mina[x + 1][y + 1] +
mina[x - 1][y] + mina[x + 1][y] + mina[x - 1][y - 1] + mina[x - 1][y + 1] -
8 * '0';
}
根据这8个坐标,再找到周围的坐标来排查,如果还没有就继续,如果有就显示雷的信息并停止
坐标周围有没有雷,进行展开
代码如下:
//如果坐标周围没有雷就进行展开
open_map(char mina[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
if (x >= 1 && x <= ROW&&y >= 1 && y <= COL)
{
int ret = Mine_amount(mina, x, y);//周围雷的数量
if (ret == 0)
{
show[x][y] = ' ';
if (show[x - 1][y] == '*'&&x - 1 > 0 && x - 1 < ROWS&&y > 0 && y < COLS)
open_map(mina, show, x - 1, y);
if (show[x - 1][y - 1] == '*'&&x - 1 > 0 && x - 1 < ROWS&&y - 1 > 0 && y - 1 < COLS)
open_map(mina, show, x - 1, y - 1);
if (show[x - 1][y + 1] == '*'&&x - 1 > 0 && x - 1 < ROWS&&y + 1 > 0 && y + 1 < COLS)
open_map(mina, show, x - 1, y + 1);
if (show[x][y - 1] == '*'&&x > 0 && x < ROWS&&y - 1 > 0 && y - 1 < COLS)
open_map(mina, show, x, y - 1);
if (show[x][y + 1] == '*'&&x > 0 && x < ROWS&&y + 1 > 0 && y + 1 < COLS)
open_map(mina, show, x, y + 1);
if (show[x + 1][y] == '*'&&x + 1 > 0 && x + 1 < ROWS&&y > 0 && y < COLS)
open_map(mina, show, x + 1, y);
if (show[x + 1][y - 1] == '*'&&x + 1 > 0 && x + 1 < ROWS&&y - 1 > 0 && y - 1 < COLS)
open_map(mina, show, x + 1, y - 1);
if (show[x + 1][y + 1] == '*'&&x + 1 > 0 && x + 1 < ROWS&&y + 1 > 0 && y + 1 < COLS)
open_map(mina, show, x + 1, y + 1);
}
else
{
show[x][y] = ret + '0';
}
}
}
6.判断是否将雷排完
每下一次坐标,都进行一次判断,判断show数组中“*”的个数是否和雷数相等 ,相等就结束游戏,不相等就继续,使用循环来检测每一个坐标
//判断是否将雷排完
int iswin(char show[ROWS][COLS], int row, int col)
{
int count = 0;
int i = 0;
int j = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
return count;
}
三.游戏效果和代码
1.游戏效果:
2.游戏代码
1.game.h头文件
#define ROW 9//mina数组的行
#define COL 9//mina数组的列
#define ROWS ROW+2//show数组的行
#define COLS COL+2//show数组的列
#define MINE 10//地雷的个数
//头文件
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//初始化mina ,show函数
void Initializ(char board[ROWS][COLS], int rows, int cols, char str);
//打印两个数组
void Disprint(char board[ROWS][COLS], int row, int col);
//放置雷
void Place_mine(char mina[ROWS][COLS], int row, int col);
//排雷并显示雷的信息
void Message_mine(char mina[ROWS][COLS], char show[ROWS][COLS], int row, int col);
2.test.c 文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"//引用头文件
void menu()//菜单
{
printf("|-----------------------------------------|\n");
printf("|-------1.开始游戏------0.退出游戏--------|\n");
printf("|-----------------------------------------|\n");
}
void game()//游戏函数
{//创建两个数组
char mina[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//将mina初始化为‘0’,将show初始化为‘*’
Initializ(mina, ROWS, COLS, '0');
Initializ(show, ROWS, COLS, '*');
//打印出数组和行号
//Disprint(mina, ROW, COL);
Disprint(show, ROW, COL);
//在mina中放置雷
Place_mine(mina, ROW, COL);
//Disprint(mina, ROW, COL);
//排雷并显示雷的信息
Message_mine(mina, show, ROW, COL);
}
void test()//函数
{
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);
}
int main()
{
test();//函数
return 0;
}
3.game.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//对两个数组进行初始化
void Initializ(char board[ROWS][COLS], int rows, int cols, char str)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = str;
}
}
}
//打印棋盘
void Disprint(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int c = 0;
for (j = 1; j <= col; j++)//打印列号
{
printf(" %d ", j);
}
printf("\n");//换行
for (c = 1; c <= col; c++)//打印上面的横杠
{
if (c == 1)
{
printf(" ");
}
printf("--- ");
}
printf("\n");//换行
for (i = 1; i <= row; i++)//打印每行的内容
{
printf("%d", i);//打印行号
for (j = 1; j <= col; j++)//打印每列的内容
{
printf(" %c", board[i][j]);
printf(" |");//打印每个字符之间的分隔竖杠
}
printf("\n");//换行
int s = 0;
printf(" ");
for (s = 1; s <= row; s++)//打印分割行
{
printf("---|");
}
printf("\n");//换行
}
printf("\n");//打印每一行之后换行
}
//放置雷
void Place_mine(char mina[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = MINE;
while (count)
{
x = rand() % ROW + 1;
y = rand() % COL + 1;
if (mina[x][y] == '0')
{
mina[x][y] = '1';
count--;
}
}
}
//周围雷的数量
int Mine_amount(char mina[ROWS][COLS], int x, int y)
{
return mina[x][y - 1] + mina[x][y + 1] + mina[x + 1][y - 1] + mina[x + 1][y + 1] +
mina[x - 1][y] + mina[x + 1][y] + mina[x - 1][y - 1] + mina[x - 1][y + 1] -
8 * '0';
}
//判断是否将雷排完
int iswin(char show[ROWS][COLS], int row, int col)
{
int count = 0;
int i = 0;
int j = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
return count;
}
//如果坐标周围没有雷就进行展开
open_map(char mina[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
if (x >= 1 && x <= ROW&&y >= 1 && y <= COL)
{
int ret = Mine_amount(mina, x, y);//周围雷的数量
if (ret == 0)
{
show[x][y] = ' ';
if (show[x - 1][y] == '*'&&x - 1 > 0 && x - 1 < ROWS&&y > 0 && y < COLS)
open_map(mina, show, x - 1, y);
if (show[x - 1][y - 1] == '*'&&x - 1 > 0 && x - 1 < ROWS&&y - 1 > 0 && y - 1 < COLS)
open_map(mina, show, x - 1, y - 1);
if (show[x - 1][y + 1] == '*'&&x - 1 > 0 && x - 1 < ROWS&&y + 1 > 0 && y + 1 < COLS)
open_map(mina, show, x - 1, y + 1);
if (show[x][y - 1] == '*'&&x > 0 && x < ROWS&&y - 1 > 0 && y - 1 < COLS)
open_map(mina, show, x, y - 1);
if (show[x][y + 1] == '*'&&x > 0 && x < ROWS&&y + 1 > 0 && y + 1 < COLS)
open_map(mina, show, x, y + 1);
if (show[x + 1][y] == '*'&&x + 1 > 0 && x + 1 < ROWS&&y > 0 && y < COLS)
open_map(mina, show, x + 1, y);
if (show[x + 1][y - 1] == '*'&&x + 1 > 0 && x + 1 < ROWS&&y - 1 > 0 && y - 1 < COLS)
open_map(mina, show, x + 1, y - 1);
if (show[x + 1][y + 1] == '*'&&x + 1 > 0 && x + 1 < ROWS&&y + 1 > 0 && y + 1 < COLS)
open_map(mina, show, x + 1, y + 1);
}
else
{
show[x][y] = ret + '0';
}
}
}
//排雷并显示雷的信息
void Message_mine(char mina[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int len = 0;
int x = 0;
int y = 0;
while (1)
{
printf("请输入坐标:\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= ROW&&y >= 1 && y <= COL&&show[x][y] == '*')
{
if (mina[x][y] == '1')
{
printf("很遗憾!你踩到雷了!!\n");
Disprint(mina, ROW, COL);
break;
}
else
{
open_map(mina, show, x, y);//如果坐标周围没有雷就进行展开
len = iswin(show, ROW, COL);//判断是否将雷排完
Disprint(show, ROW, COL);
if (len == MINE)
{
printf("恭喜你!排雷成功!!\n");
Disprint(mina, ROW, COL);
break;
}
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
}
总结
以上就是本篇 <扫雷>所有内容,需要知道扫雷游戏的原理和结构思路,然后再分成一个个小步实现功能,最后整合在一起并做一些修改和完善来实现游戏的功能。注:游戏代码可以在vs编译器上完美运行!