c语言-实战项目:扫雷

在学习了C语言数组和函数后,我们就可以做一个有意思和非常经典的游戏——扫雷,那么就跟随这一篇文章来看看是如何实现扫雷的吧,在正式开始之前我们先分析一下扫雷是如何实现的 

扫雷游戏分析和设计

扫雷游戏的功能说明

  1. 使⽤控制台实现经典的扫雷游戏
  2. 游戏可以通过菜单实现继续玩或者退出游戏
  3. 扫雷的棋盘是9*9的格⼦
  4. 默认随机布置10个雷
  5. 可以排查雷
    • 如果位置不是雷,就显⽰周围有⼏个雷
    • 如果位置是雷,就炸死游戏结束
    • 把除10个雷之外的所有雷都找出来,排雷成功,游戏结束

那我们最终的游戏界面是这样子的:

游戏的分析和设计

数据结构的分析

这是一个扫雷游戏的布局,我们可以发现整个棋局是由9*9大小的方框组成的,那么此时我们就可以想到,可以利用到数组来实现

在这张图中,我们顺便点了一处,然后就摊开一片,并且有数字,那我们可以想到:

  1. 如果排查的位置不是雷,那么就显示这个坐标周围有几个雷
  2. 如果排除的位置是雷,就被炸死了,那么游戏就结束了

 那么再由失败的界面我们可以看到一共有10个雷,那么如果把10个雷全部排查出来,游戏就胜利了

那么我们来解决雷的问题,我们排查一个坐标时,那个坐标是不是雷,那么就需要一个9*9的二维数组来记录了  

                                                                                              布置雷的棋盘

我们假设:1是雷0不是雷

那么当我们点击一个位置时,就应该显示周围有几个雷,但是如果周围是一个雷的话,那么显示的也是1,此时就会有歧义,这个到底是雷还是周围的雷?所以我们可以在创建一个二维数组来专门存放排查出的雷的信息

那么此时左边的这个图的数组就可以安心的存放雷的信息,右边这个图的数组就专门来显示排查出雷的信息

这个时候雷的棋盘还有一个问题,在扫雷游戏中, 如果排查的不是雷,是会显示的周围8个坐标是否是雷的信息的,那么此时我们设计的棋局,如果排查到边缘,也应该去排查周围8个坐标的信息,但是这样二维数组就越界了

所以我们不能创建9*9的棋局,而且是11*11的棋局,最外围的11行11列不去布置雷,放0,这样我们排查边缘上的坐标就不担心越界了,相同的道理,排查出的雷的信息这个棋局也应该设置为11*11的布局

                                                           布置雷                                                       排查出雷的信息

  • 那我们将布置好雷的信息放入一个叫做mine数组中
  • 将排查出的雷的信息放入一个叫做show数组中

  • 那我们在最初还没有开始放雷的情况下,应该将mine数组全部初始化为0
  • 对于show来说,最初一个雷都还没有排查的时候,应该全部放*

为了方便打印,因为show数组放的是字符 * 所以我们也将数字0 变成字符 0,同样雷也是字符1,排查后显示周围有几个雷的数字也改为数字字符

在正式进入代码前我们将代码分为三个文件进行存储:

  1. test.c—测试游戏逻辑 游戏相关的其他逻辑放在这里
  2. game.c—游戏实现
  3. game.h—游戏函数的声明

其中会涉及到的函数:

  1. menu()—放菜单
  2. game()—放游戏主体
  3. char mine[ROWS][COLS];—存放雷的信息
  4. char show[ROWS][COLS];—存放排查雷的信息
  5. InitBoard—初始化棋盘
  6. DisplayBoard—打印棋盘
  7. SetMine—实现布置雷
  8. FindMine—排查雷
  9. GetMineCount—显示坐标周围的雷

那我们进入代码:

test.c

#include <stdio.h>
#include "game.h"

void menu()
{
	printf("*************************\n");
	printf("*****   1.玩游戏     ****\n");
	printf("*****   0.退出游戏  *****\n");
	printf("*************************\n");
}

void game()
{
	char mine[ROWS][COLS];
	char show[ROWS][COLS]; 
	InitBoard(mine,ROWS,COLS,'0'); 
	InitBoard(show, ROWS, COLS,'*'); 
	//棋盘的打印
	DisplayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, 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退出游戏\n");
			break;
		default:
			printf("请输入正确的值!\n");
			break;
		}
	}
	while (input); 
	{		
	}
	return 0;
}

详解:从main函数开始,按照游戏的运行逻辑来一一解释

#include "game.h":调用game.h的头文件

main

  1. int input = 0:用户选择菜单,之所以放在外面是为了更加全局一点,让while好判断
  2. srand((unsigned int)time(NULL)):定义随机值让后面布置的雷随机
  3. do while:用do while循环来让用户进行菜单的选择,如果选择错误或游戏可以重新选择
  4. menu()调用菜单
  5. scanf("%d", &input):存放用户输入的值
  6. switch (input):根据用户输入不同,进入不同的结果
  7. game()调用游戏的主体
  8. break只会跳出Switch
  9. default如果用户输入了非0非1的其他值,要求用户重新输入,那么既然不会是0了那么就不会退出while循环的
  10. while (input)如果用户选择0,那么跳出Switch,来到while,while判断为假,停止循环,退出游戏

menu

单纯的打印菜单

game

  1. 首先先创建两个数组:
  • char mine[ROWS][COLS]:布置雷
  • char show[ROWS][COLS]:排查雷的信息
  1. InitBoard(mine,ROWS,COLS,'0')调用初始化函数,'0'表示需要初始化为字符0,我们传参过去,方便依照这个初始化
  2. InitBoard(show, ROWS, COLS,'*')'*'表示需要初始化为字符*,我们传参过去,方便依照这个初始化

棋盘的打印

  1. DisplayBoard(show, ROW, COL)只需打印中间的就行了,所以我们只需要 9*9 那么传参就不用传带s的了

布置雷

  1. SetMine(mine, ROW, COL):将关于雷的数组传参过去,布置也只需要9*9的就行了什么我们不用传带s的了
  2. DisplayBoard(mine, ROW, COL):这个可以打印出来看一下雷布置在哪里

排查雷

  1. FindMine(mine,show,ROW,COL)排查雷既需要mine数组的雷的信息,也要show排查后的信息,然后排查雷只需9*9就行了

game.h

这里面主要是放的函数的声明

代码:

#include <stdio.h>

#include <stdlib.h>//因为主函数要调用game.h这个头文件,所以在这里调用,也相当于在test.c中调用
#include<time.h>

//用define来定义关键字,可以在后期方便改变游戏难度,例如:初级,中等,高级等..... 
//定义在头文件是为了方便调用
#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);//接收传过来的参数,set用来接收mine和show需要初始化什么

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int rows, int cols);

//注意:我们在传参的时候,可以传11*11,但是操作的时候只操作9*9就行了
//布置雷
#define EASY_COUNT 10 //设雷为10个
void SetMine(char mine[ROWS][COLS], int row, int col);

//排查雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col);

 详解在代码中的注释里

game.c

#include <stdio.h>
#include "game.h"//调用game.h的头文件

//函数的实现


//初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
	int i = 0;
	for ( i = 0; i <rows; i++) 
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			//mine 初始化为0
			//show 初始化为*
			board[i][j] = set;
		}
	}
}



//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("————扫雷游戏————\n");
	for (i = 0; i <= row; i++) //打印列标
	{
		printf("%d ", i);
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);//打印行标
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]); 
		}
		printf("\n"); 
	}
	printf("————扫雷游戏————\n\n\n");
}



//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col) 
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] =='0') 
		{
			mine[x][y] ='1';
			count--;
		}
	}
}


//排查雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	//让8个方位的坐标加起来,并且返回
	return( mine[x - 1][y - 1] +
			mine[x][y - 1] +
			mine[x + 1][y - 1] +
			mine[x + 1][y] +
			mine[x + 1][y + 1] +
			mine[x][y + 1] +
			mine[x - 1][y + 1] +
			mine[x - 1][y]-8*'0'); 
}



void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 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') 
			{
				printf("\n\n\n排查的是雷!游戏失败!\n\n\n");
				DisplayBoard(mine, ROW, COL); 
				break;
			}
			else
			{
				int count=GetMineCount(mine, x, y);
				show[x][y]=count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
		printf("请输入合法的坐标!\n");
		}
	}
	if (win == row * col - EASY_COUNT) 
	{
		printf("恭喜你,游戏胜利!\n");
		DisplayBoard(mine, ROW, COL);
	}
}

详解:

InitBoard

初始化函数:

  1. void InitBoard(char board[ROWS][COLS], int rows, int cols,char set):接收test中的game传过来的参数,用board接受mine和show,行接收行,列接受列,set接收需要初始化的类型
  2. int i = 0;for ( i = 0; i <rows; i++):初始化行
  3. int j = 0; for (j = 0; j < cols; j++):初始化列
  4. board[i][j] = set:接收传参过来的set,mine传参过来的是字符0,那么初始化为字符0,show传参过来的是*,那么初始化为*

DisplayBoard

打印棋盘函数:

  1. void DisplayBoard(char board[ROWS][COLS], int row, int col):接收test中的game传过来的参数,用board接受show,行接收行,列接受列,因为只需要打印中间的棋盘,不需要打印外围的大数组棋盘,所以就行列只用接收9*9就行了
  2. for (i = 0; i <= row; i++):打印列标,让i=0是因为,i=1的话列标对应会错位,第8列会显示9
  3. printf("\n")打印完列标后换行,准备打印棋盘
  4. for (j = 1; j <= col; j++):初始化为1,是因为大数组11*11,我们只显示9*9,所以第一个格子的二维数组坐标就是[1][1]
  5. printf("%c ", board[i][j]):打印棋盘内容
  6. printf("\n"):打印一行后换行

SetMine

布置雷函数:

  1. void SetMine(char mine[ROWS][COLS], int row, int col):接收test中的game传过来的参数,用mine接受show,行接收行,列接受列,因为只需要将雷布置到中间的9*9就行了,不需要布置在外围的大数组棋盘,所以就行列只用接收9*9就行了
  2. int count = EASY_COUNT:定义一个count来接受game.h中设定的10个雷
  3. while (count):开始随机坐标布置雷
  4. int x = rand() % row + 1int y = rand() % col + 1:&row是让雷的范围为0~9,加上1就是1~10,将雷随机布置到x,y坐标处

  5. if (mine[x][y] =='0'):判断mine数组中雷的情况,如果该坐标没有布置雷,那么就布置一颗雷,如果布置了,那么不在此位置布置雷了
  6. count--:每布置一颗雷就减少一颗,总共10颗

GetMineCount

排查雷函数:

  1. int GetMineCount(char mine[ROWS][COLS], int x, int y):排查周围8个方格的雷, 接收game.c中的FindMine传过来的参数,用mine接受mine,行接收行,列接受列,因为只需要将雷布置到中间的9*9就行了,不需要布置在外围的大数组棋盘,所以就行列只用接收9*9就行
  2. return( mine[x - 1][y - 1] +mine[x][y - 1] +mine[x + 1][y - 1] + mine[x + 1][y] +mine[x + 1][y + 1] +mine[x][y + 1] +mine[x - 1][y + 1] +mine[x - 1][y]-8*'0'):让数字变为数字字符(在show[x][y]中会有解释),让8个方位的坐标加起来,并且返回

  3. void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col):排查雷既需要mine数组的雷的信息,也要show排查后的信息,所以接受test.c的game传来的两个数组,然后排查雷只需9*9就行了
  4. int x = 0;int y = 0;int win = 0:定义x和y作为坐标,win作为游戏胜利的判断条件
  5. while (win<row*col-EASY_COUNT):开始循环的进行排查雷,row*col-EASY_COUNT是9*9-10=71也就是不是雷的格子,如果win小于71,那么就进入循环
  6. if (x >= 1 && x <= row && y >= 1 && y <= col):判断用户输入坐标的合法性
  7. if (mine[x][y] == '1'):看用户排查的坐标是不是雷
  8. DisplayBoard(mine, ROW, COL):游戏失败后,将雷全部显示出来
  9. Else:如果不是雷,在该坐标显示周围的雷的个数
  10. int count=GetMineCount(mine, x, y):调用函数去访问mine数组来获取当前x,y周围有几个雷,统计出来赋予count
  11. show[x][y]=count + '0':我们知道,数字字符0是从48开始的,那就有个规律,假如说字符3减去字符1那么就是'3'-'0'=51-48=3 等于数字3,那我们这里假设统计出来雷的个数count为数字1,那么让它加上一个字符0,就可以得到字符1了,再将字符放入排查出的雷的信息show数组中,就可以在该坐标显示周围的雷的个数了
  12. DisplayBoard(show, ROW, COL)后打印show数组,就可以让用户看到当前坐标的信息了
  13. win++:每一次排查成功我们就让不是雷的格子自增1,如果不雷的格子大于了71,说明雷排除完了,游戏就赢了
  14. if (win == row * col - EASY_COUNT):如果不是雷的格子等于71了,那么说明雷排除完了游戏赢了
  15. DisplayBoard(mine, ROW, COL):游戏胜利后将雷的位置全部显示出来

好了到这里就结束了

游戏还不完善比如说:不能展开一大片,不能标记,不能用鼠标点击,还不能设置游戏等级等

这些在后期会跟新出来的...

如有错误请指出

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值