提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
提示:这里可以添加本文要记录的大概内容:
这是一个用C语言写出来的扫雷小游戏,重难点在于递归与数组的运用。
提示:以下是本篇文章正文内容,下面案例可供参考
一、创建阶段
项目比较简单,我们在VS_CODE里面,在自己的项目文件夹下,创建SL_GAME.c文件,用于游戏逻辑的测试,包含游戏菜单的打印,游戏设计的基本逻辑的展示。此外还有fun.c文件,用于实现游戏所需要用到的函数,还有一个fun.h文件,用于存放此项目所用到的所有头文件,以及函数的声明。
二、具体实现
1,先把菜单打印出来,确定主体框架:
void menu(){
printf("**************************************\n");
printf("*************1,play 0,exit***********\n");
printf("**************************************\n");
}
int main(){
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("输入错误");
break;
}
}while (input);
}```
2,开始实现游戏
我们这里需要一个棋盘,考虑到后面我们要存放埋雷的信息和排雷时候的信息,我们最好准备2个棋盘。这里有个ROWS和COLS,是为了防止最边上棋子排雷时出现数组越界。
fun.h
#ifndef _FUNC_H_
#define _FUNC_H_
#define ROW 9
#define COL 9
#define EASY_COUNT 3
#define ROWS ROW+2
#define COLS COL+2
void SetMine(char board[ROWS][COLS],int row,int col,int err);
void InitBoard(char board[ROWS][COLS],int rows,int cols,char set);
void DisplayBoard(char board[ROWS][COLS],int rows,int cols);
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col);
int get_mine_count(char board[ROWS][COLS],int x,int y);
void ExplosionSpread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pw);
void MarkMine(char board[ROWS][COLS], int row, int col);
#endif
SL_GAME.c
void game(){
char show[ROWS][COLS]={0};//存放埋雷信息
char mine[ROWS][COLS]={0};//存放排雷的信息
InitBoard(mine,ROWS,COLS,'0');
InitBoard(show,ROWS,COLS,'*');
//设置雷,假设第一个踩雷
int err=0;
SetMine(mine,ROW,COL,err);
//DisplayBoard(mine,ROW,COL);
DisplayBoard(show,ROW,COL);
//排查
FindMine(mine,show,ROW,COL);
}
定义好数组后,我们就开始对棋盘进性初始化,埋雷盘先全部初始化为0,然后用随机函数进行埋雷,而展示出来给玩家看到就是用 *,表示。
void InitBoard(char board[ROWS][COLS],int rows,int cols,char set){
int i=0;
int j=0;
for(i=0;i<rows;i++){
for(j=0;j<cols;j++){
board[i][j]=set;
}
}
}
接下来我们进行埋雷,用rand函数随机埋,这里减去的err是预防玩家第一次玩就炸雷,用于后面补上埋的一个雷用的。埋好雷如下(这里我只设置了3个):
代码实现:
void SetMine(char board[ROWS][COLS],int row,int col, int err){
int count=EASY_COUNT-err;
while(count){
int x=rand()%row+1;
int y=rand()%col+1;
if(board[x][y]=='0'){
board[x][y]='1';
count--;
}
}
}
埋好雷之后,我们就要包装好,打印出来给玩家玩,这里用到DisplayBoard()函数。
代码实现:
void DisplayBoard(char board[ROWS][COLS],int row,int col){
int i=0;
int j=0;
printf("------------扫雷游戏-------------\n");
for(j=0;j<=col;j++){//打印列号
printf("%d ",j);
}
printf("\n");
for(i=1;i<=row;i++){
printf("%d ",i);//打印行号
for(j=1;j<=col;j++){
printf("%c ",board[i][j]);
}
printf("\n");
}
printf("------------扫雷游戏-------------\n");
}
这样我们就可以开始进行排雷了,我们玩过排雷游戏的都知道,当我们点开一个格子时,会提醒我们以这个格子为中心的一个九宫格内有多少个雷,而且会把连着的一部分没类的地区散开来,并且雷区边缘等格子也会提醒你周围有多少雷,而且你第一次点的那个不能是雷。我们定义个排雷函数:
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col){
int x=0;
int y=0;
int win=0;//找到非0的个数
int *pw=&win; // 用于计算是否赢了
int err = 0; //第一次踩雷后+1,重新刷雷
int first = 0; //判断是否第一次踩雷
char ch = 0; //用来接受是否需要标记雷
while (win<row*col-EASY_COUNT)
{
printf("请输入要排查雷的坐标:");
scanf("%d %d",&x,&y);
if(x>=1 && x<=row && y>=1 &&y<=col){
if(mine[x][y]=='1'&& 0==first){ //第一次就踩雷
mine[x][y]='0';
err=EASY_COUNT-1; //补上一个雷
SetMine(mine,ROW,COL,err);
ExplosionSpread(mine, show, row, col, x, y, pw); //扩散
DisplayBoard(show,ROW,COL); //打印
first++;
}
if(show[x][y]!='*'){
printf("该数组已经被排查过了,不能重复排查!\n");
}
else{
if(mine[x][y]=='1' ){
printf("你被炸死了\n");
DisplayBoard(mine,ROW,COL); //输了就打印mine数组,让玩家知道自己踩雷了
break;
}
//不是雷,统计
else{
ExplosionSpread(mine, show, row, col, x, y, pw); //爆炸展开一片
// win++;
//统计周围有几个雷
// int count= get_mine_count(mine,x,y);
// show[x][y]=count+'0';//转换成数字字符
DisplayBoard(show,ROW,COL);
printf("需要标记雷的位置请输入y/Y,否则请按任意键->");
while ((ch = getchar()) != '\n'); //清理缓冲区
scanf("%c", &ch);
if (ch == 'Y' || ch == 'y')
{
MarkMine(show, row, col); //标记雷
DisplayBoard(show, row, col);
}
}
}
}
else{
printf("输入的坐标非法,请从新输入:\n");
} /* code */
}
if(win==row*col-EASY_COUNT){
printf("你已经排雷成功!你赢了!\n");
DisplayBoard(mine,ROW,COL);
}
}
这里面比较重点的是散开函数,我们是这里用到了递归,以及计算周围有多少个雷,这里面我们由于把雷标成字符1,没雷是字符0,那么直接找这个格子其余八个格子的减去字符0,就可以得出有多少个雷,(字符1和字符0相差1)。还有如何标记雷,我们直接把show出来的 * 换成!即可。
在这里插//递归爆炸式展开一片
void ExplosionSpread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pw)
{
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断坐标是否为排查范围内
{
int num = get_mine_count(mine, x, y); //获取坐标周围雷的个数
if (num == 0)
{
(*pw)++;
show[x][y] = ' '; //如果该坐标周围没有雷,就把该坐标置成空格,并向周围八个坐标展开
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*') //限制递归条件,防止已经排查过的坐标再次递归,从而造成死递归
ExplosionSpread(mine, show, row, col, i, j, pw);
}
}
}
else
{
(*pw)++;
show[x][y] = num + '0';
}
}
}
//算雷
int get_mine_count(char board[ROWS][COLS],int x,int y){
return board[x-1][y]+
board[x-1][y-1]+
board[x][y-1]+
board[x+1][y-1]+
board[x+1][y]+
board[x+1][y+1]+
board[x][y+1]+
board[x-1][y+1]-8*'0';
}
```//标记雷的位置
void MarkMine(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入你想要标记位置的坐标->");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断该坐标是否合法
{
if (board[x][y] == '*') //判断该坐标是否被排查
{
board[x][y] = '!';
break;
}
else
{
printf("该位置不能被标记,请重新输入!\n");
}
}
else
{
printf("输入错误,请重新输入!\n");
}
}
}
最后附上完整代码:
SL_GAME.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include "SL_GAME.h"
#include"func.h"
void menu(){
printf("**************************************\n");
printf("*************1,play 0,exit***********\n");
printf("**************************************\n");
}
void game(){
char show[ROWS][COLS]={0};//存放埋雷信息
char mine[ROWS][COLS]={0};//存放排雷的信息
InitBoard(mine,ROWS,COLS,'0');
InitBoard(show,ROWS,COLS,'*');
//设置雷,假设第一个踩雷
int err=0;
SetMine(mine,ROW,COL,err);
//DisplayBoard(mine,ROW,COL);
DisplayBoard(show,ROW,COL);
//排查
FindMine(mine,show,ROW,COL);
}
int main(){
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("输入错误");
break;
}
}while (input);
}
SL_GAME.h///可有可无
#ifndef _SL_GAME_H_
#define _SL_GAME_H_
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//void InitBoard(char board[ROWS][COLS],int rows,int cols,char set);
#endif
fun.c
#include<stdio.h>
#include "SL_GAME.h"
#include "func.h"
#include<stdlib.h>
void InitBoard(char board[ROWS][COLS],int rows,int cols,char set){
int i=0;
int j=0;
for(i=0;i<rows;i++){
for(j=0;j<cols;j++){
board[i][j]=set;
}
}
}
void DisplayBoard(char board[ROWS][COLS],int row,int col){
int i=0;
int j=0;
printf("------------扫雷游戏-------------\n");
for(j=0;j<=col;j++){//打印列号
printf("%d ",j);
}
printf("\n");
for(i=1;i<=row;i++){
printf("%d ",i);//打印行号
for(j=1;j<=col;j++){
printf("%c ",board[i][j]);
}
printf("\n");
}
printf("------------扫雷游戏-------------\n");
}
void SetMine(char board[ROWS][COLS],int row,int col, int err){
int count=EASY_COUNT-err;
while(count){
int x=rand()%row+1;
int y=rand()%col+1;
if(board[x][y]=='0'){
board[x][y]='1';
count--;
}
}
}
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col){
int x=0;
int y=0;
int win=0;//找到非0的个数
int *pw=&win;
int err = 0; //第一次踩雷后+1,重新刷雷
int first = 0; //判断是否第一次踩雷
char ch = 0; //用来接受是否需要标记雷
while (win<row*col-EASY_COUNT)
{
printf("请输入要排查雷的坐标:");
scanf("%d %d",&x,&y);
if(x>=1 && x<=row && y>=1 &&y<=col){
if(mine[x][y]=='1'&& 0==first){ //第一次就踩雷
mine[x][y]='0';
err=EASY_COUNT-1; //补上一个雷
SetMine(mine,ROW,COL,err);
ExplosionSpread(mine, show, row, col, x, y, pw); //扩散
DisplayBoard(show,ROW,COL); //打印
first++;
}
if(show[x][y]!='*'){
printf("该数组已经被排查过了,不能重复排查!\n");
}
else{
if(mine[x][y]=='1' ){
printf("你被炸死了\n");
DisplayBoard(mine,ROW,COL); //输了就打印mine数组,让玩家知道自己踩雷了
break;
}
//不是雷,统计
else{
ExplosionSpread(mine, show, row, col, x, y, pw); //爆炸展开一片
// win++;
//统计周围有几个雷
// int count= get_mine_count(mine,x,y);
// show[x][y]=count+'0';//转换成数字字符
DisplayBoard(show,ROW,COL);
printf("需要标记雷的位置请输入y/Y,否则请按任意键->");
while ((ch = getchar()) != '\n'); //清理缓冲区
scanf("%c", &ch);
if (ch == 'Y' || ch == 'y')
{
MarkMine(show, row, col); //标记雷
DisplayBoard(show, row, col);
}
}
}
}
else{
printf("输入的坐标非法,请从新输入:\n");
} /* code */
}
if(win==row*col-EASY_COUNT){
printf("你已经排雷成功!你赢了!\n");
DisplayBoard(mine,ROW,COL);
}
}
//标记雷的位置
void MarkMine(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入你想要标记位置的坐标->");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断该坐标是否合法
{
if (board[x][y] == '*') //判断该坐标是否被排查
{
board[x][y] = '!';
break;
}
else
{
printf("该位置不能被标记,请重新输入!\n");
}
}
else
{
printf("输入错误,请重新输入!\n");
}
}
}
//算雷
int get_mine_count(char board[ROWS][COLS],int x,int y){
return board[x-1][y]+
board[x-1][y-1]+
board[x][y-1]+
board[x+1][y-1]+
board[x+1][y]+
board[x+1][y+1]+
board[x][y+1]+
board[x-1][y+1]-8*'0';
}
//基础功能
//1,标记功能
//2,
//递归爆炸式展开一片
void ExplosionSpread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pw)
{
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断坐标是否为排查范围内
{
int num = get_mine_count(mine, x, y); //获取坐标周围雷的个数
if (num == 0)
{
(*pw)++;
show[x][y] = ' '; //如果该坐标周围没有雷,就把该坐标置成空格,并向周围八个坐标展开
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*') //限制递归条件,防止已经排查过的坐标再次递归,从而造成死递归
ExplosionSpread(mine, show, row, col, i, j, pw);
}
}
}
else
{
(*pw)++;
show[x][y] = num + '0';
}
}
}
fun.h
#ifndef _FUNC_H_
#define _FUNC_H_
#define ROW 9
#define COL 9
#define EASY_COUNT 3
#define ROWS ROW+2
#define COLS COL+2
void SetMine(char board[ROWS][COLS],int row,int col,int err);
void InitBoard(char board[ROWS][COLS],int rows,int cols,char set);
void DisplayBoard(char board[ROWS][COLS],int rows,int cols);
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col);
int get_mine_count(char board[ROWS][COLS],int x,int y);
void ExplosionSpread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pw);
void MarkMine(char board[ROWS][COLS], int row, int col);
#endif
总结
这个游戏不难,此外还有很大的改进空间,此外,要是想屏幕干净些,也可以在适当位置加上system(“cls”)语句,用来清空屏幕。