C语言--扫雷游戏(第一个不会炸+展开+标记+完整源码)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

这是一个用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”)语句,用来清空屏幕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值