C语言完成扫雷游戏以及进阶
大家好,今天我来为大家分享一下用C语言来完成一个扫雷游戏程序的文章。相信大家都玩过或者了解过,反正我很早就接触过扫雷,但是当时不懂规则或者玩法,所以每次扫个一两次就被炸死了,到后来才明白怎么个玩法,今天呢我们就来试着做一个简洁版的扫雷游戏
一.思路解析:
1.开始菜单
首先我们在写代码之前先理清一下思路,大家拥有电脑后应该都玩过扫雷亦或是什么蜘蛛卡牌之类的,当热这些都是题外话。这些游戏或者说很多游戏都会在开始界面给你一个选择菜单,比如设置,开始游戏,退出游戏等,当然我们今天用到的菜单比较简洁值需要开始和退出就可以,所以我们首先先要做一个菜单,让玩家选择玩或退出或者玩完之后继续游玩。而在选择进入游戏后,玩家就要开始游玩了,那么这时候就要基于扫雷游戏的规则去编写程序了。
2.游戏程序
在开始之前,我们回想一下,游玩扫雷游戏时的情景以及扫雷的规则。我们知道扫雷是在一个类棋盘的基础上进行排雷的,这时候就得想象一下 ,这个棋盘应该如何布置,雷应该如何配置在那么一个大的棋盘中。我们又是如何一个一个将雷排查出从而扫雷成功的。
要解决这几个问题,我么可以分成三步:初始化棋盘并将棋盘扫雷状况显示在屏幕上、将雷随机安置在棋盘的有效位置以及玩家游玩时排雷的过程对棋盘各个坐标的排除
2-1:初始化
在这一步,我么需要对棋盘进行初始化,但是,应为扫雷要配置雷到棋盘上,而又要把扫雷进程显示在玩家屏幕上,这样才能知道自己排了多少,是否全部排完,但这样就冲突,我们总不能布置完雷,又把这个棋盘显示,那这样还排啥,都在屏幕上了,所以我们可以初始化两个棋盘,一个给玩家展示的,一个布置雷的。而要完成这一步,我们就需要两个数组,棋盘大小的行数和列数。在这里,我采用,雷用‘1’表示无雷是‘0’,展示用‘*’。
2-2:布置雷
布置雷首先要考虑好有效性,即安置在棋盘内而不是外,然后很重要的一点是”随机“,这点很重要,不然你猜也猜得到他们都是会以规律布置的。这一点就需要用到#include<time.h>这一库函数了。而布置雷的操作,我们只要在随机情况下保准在棋盘内检测是否数组元素也就是棋盘坐标进行判断若这些坐标为0,就将他们置为1,循环你选择的难度来安置雷个数。
2-3:排雷
玩家排雷,首先要输入排查的点位,但是直接输入不够直观,我们可以打印出行数和列数以便玩家进行分辨。然后进行循排查知道全部排完或你被炸死。在过程中要有变量来记录你已经排雷坐标。在这之后,做判断看雷是否排完,是则跳出循环结束本局,否则继续循环排雷。然后判断你是否碰雷是则游戏失败结束本局游戏,否则,继续判断,如果这个坐标==*则对这坐标周围八个坐标进行计数雷的个数并显示在当前坐标,随后++已排坐标,进入下一轮循环。
二.代码实现:
思路分析完后,就应该进行代码编写了。
在这之前,要讲一个重要的事——就是模块化编程或者叫多文件编程,这个在等下实现代码中就能理解了。
按照之前思路分析一样,我们首先要在主程序里写一个菜单模块,菜单打印可以用函数来封装一下。


我们使用do-while循环来实现多次游戏的功能,使用switch语句让玩家决定是否开始游戏。
如果玩家输入1-进入游戏,程序则跳转到game函数——与之前所说的,我们要在这个函数内实现初始化,布置雷,排雷的功能

在这里我们,定义两个数组,以及初始化棋盘函数,但是在这个主程序实现的话,就会使得代码会很长很对,这时候就要用到模块化编程了,就是我们把这个游戏主体放在另一个文件game.c中实现,但我们要在主程序使用这个.c的话,就和我们使用C的库函数一样,必须引头文件,也就是说我们还得在另一个文件中加入一个.h头文件。具体实现如下

在game.c中我们实现刚刚说到的三个功能,而在,h中则是对其中的函数进行声明,以及定义一下之前初始化棋盘时数组的大小。


然后就可以在.c中实现InitBoard的功能

这个时候数组中ROWS COLS是我们在头文件里定义的要使用就得包含头文件,这时包含就得用双引号来实现即#include"game.h",这是我们自己建立的头文件,而我们要在主程序使用也只需要包含一下就可以了。
而其它代码就是很简单的初始化数组了,看看就懂了
初始完棋盘后我们还要打印棋盘到屏幕上给玩家看这是就可以封装一个函数ShowBoard用来展示雷棋盘和游玩棋盘。
同样的现在,头文件下声明一下,再到主程序里调用一下,最后在.c实现代码功能



在这个函数中我们就是实现了对棋盘的打印。
在这之后我们就要进行雷的随机布置了,这是就要用到我们之前说的库函数time.h
同样的步骤,声明,调用,写代码。


布置雷之前首先要确定这把游戏雷的个数(也可以简单理解一下调整难度嘛)
在头文件下 定义雷的个数,这样主要是方便调整,这样局部需要到.c 中一处一处改了


在这里,rand()就是使用了time.c中的函数来随机坐标布置雷。
最后就是来到排雷的函数程序了。



在这里 GetMineCount函数是用来计算玩家输入坐标,这个坐标周围八个坐标雷的个数具体实现如下:

到这里我们这个初级扫雷程序功能就已经完成了,但是还是存在一些问题,比如:其实游戏获胜条件其实是排完除雷外所有坐标,比如1个雷你就必须排完其它80坐标,这样并不是真正的扫雷游戏的功能,这就需要进阶的调整和加入其他程序来完善了。不过这里先给出初级的完整代码。
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu(void)
{
printf("**************************\n");
printf("****** 1.play ******\n");
printf("****** 2.exit ******\n");
printf("**************************\n");
}
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
//初始化棋盘
InitBoard(mine,ROWS,COLS,'0');
InitBoard(show, ROWS,COLS, '*');
//展示棋盘和完整扫雷2棋盘
ShowBoard(show,ROW,COL);
//随机配置雷的位置
BuildMine(mine,ROW,COL);
//进行排雷
FindMine(mine,show,ROW,COL);
}
int main()
{
int input = 0;
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("已退出游戏");
break;
default:
printf("输入错误,请重新输入");
break;
}
} while (input);
return 0;
}
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 EASY_COUNT 1
//函数声明
void InitBoard(char arr[ROWS][COLS], int r, int c, char Build);
void ShowBoard(char arr[ROWS][COLS], int r, int c);
void BuildMine(char mine[ROW][COL], int r, int c);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c);
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void InitBoard(char arr[ROWS][COLS], int r, int c, char Build)
{
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
arr[i][j] = Build;
}
}
}
void ShowBoard(char arr[ROWS][COLS],int r,int c)
{
printf("-------扫雷游戏开始--------\n");
for (int i = 0; i <= c; i++)
{
printf("%d ", i);//打印列号
}
printf("\n");
for (int i = 1; i <= r; i++)
{
printf("%d ", i);//打印行号
for (int j = 1; j <= c; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
void BuildMine(char mine[ROWS][COLS],int r,int c)
{
int m = EASY_COUNT;
while (m)
{
int i = rand() % r + 1;
int j = rand() % r + 1;
if (mine[i][j] == '0')
{
mine[i][j] = '1';
m--;
}
}
}
static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
return mine[x-1][y-1] + mine[x-1][y] + mine[x-1][y+1]
+ mine[x][y-1] + mine[x][y+1]+ mine[x+1][y-1]
+ mine[x+1][y] + mine[x+1][y+1] - 8 * '0';
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c)
{
int x = 0;
int y = 0;
int victory = 0;
while (victory < r * c - EASY_COUNT)
{
printf("请选择你要排查的坐标\n");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= r && y >= 1 && y <= c)
{
if (mine[x][y] == '1')
{
printf( "扫雷失败,你被炸死了!\n" );
ShowBoard( mine, r , c);
break;
}
else
{
if (show[x][y] == '*')
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
ShowBoard(show, r, c);
victory++;
}
else
{
printf("请不要重复排查\n");
}
}
}
else
{
printf("输入无效范围,请重新输入\n");
}
}
if(victory == r * c - EASY_COUNT)
{
printf("恭喜你,排雷成功!\n");
ShowBoard(mine, r, c);
}
}
番外,扫雷——递归展开:
核心就是当玩家输入的排雷坐标时,调用getmine函数对其周围八个坐标检测,若这八个坐标均为‘0’ ,即这八个坐标周围都没有雷,就将其展开,同时函数内部再自调用,也就是递归getmine函数再对这八个坐标的周围八个坐标检测,如果这八个坐标还是‘0’就继续展开,是非‘0’就不展开,直到这些展开的坐标周围都有雷,就停止下来不再展开。
这里我只分享getmine函数的实现,其它其实变动不大,就交给大家自行完成了。
void GetMineCount(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y,int row,int col,int *pvictory)
{
*pvictory = *pvictory - 1;//每次成功,值减少
int count = 0;//用来统计地雷
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)//循环范围3*3大小
{
count += (mine[i][j] - '0');
}
}
show[x][y] = count + '0';
if (count == 0)//判断该做吧周围有无地雷,没有进入递归程序
{
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)//展开周围一圈坐标;
{
if (i >= 1 && i <= row && j >= 1 && j <= col && show[i][j] == '*')//是否在棋盘内有效位置,且未被排查过
GetMineCount(show, mine, i, j, COL, ROW, pvictory);
}
}
}
}
这就是修改后的进阶getmine,逻辑是先对输入的坐标检测并展开周围非‘0’坐标,再调用对展开的坐标检测;
三.结语:
今天扫雷游戏的实现就到这里,这也是我第一次写一个比较大工程代码的博客,希望大家有所收获,写的不好的地方也请大家指出一同进步!!

1万+

被折叠的 条评论
为什么被折叠?



