一个简单扫雷游戏的实现

一个简单扫雷游戏的实现

1. 概念

要实现一个扫雷首先要知道扫雷是怎样玩的,我们可以将游戏过程做一个描述:
  游戏开始时能看一个被划为很多方块的面板,当用户点击或输入面板上的某一个方块的坐标时,该方块会被翻开。(第一次输入时不会踩到雷
如果翻开的位置是雷,则游戏结束,如果不是雷,则 该方块会变为其周围八个方块中地雷个数的总和。当地雷数的和为0时,则直接翻开周围的8个方块并将其变为 其周围方块中雷数目的总和,如果被翻开的地方仍为0,则重复此过程直到被翻开的位置周围地雷总数不为0。
  当剩下的未翻开的位置全为雷时,用户胜利,游戏结束。

2. 开始前的准备

了解了扫雷的概念后我们就可以来思考如何实现了。

  1. 设置面板
    首先我们需要一个可以使用户能够看见游戏面板。这可以用一个二维数组来表示,并在里面赋上符号“*”。
    然而只有游戏面还不够,我们还需要一个隐藏起来的面板用来存放雷的坐标,这样当用户在显示面板中输入一个坐标时,只需要看该坐标在雷面板中是否有雷就可以知道是否踩到了雷。如下:
	char show[ROWS][COLS];					//显示面板
	char mine[ROWS][COLS];					//雷面板
	memset(show, '*', sizeof(show));		//设置两个面板
	memset(mine, '0', sizeof(mine));		

2. 埋雷
   接下来我们需要在雷面板里存入雷的信息。此时我们存入的信息必须是随机的,每次打开游戏时雷所在的位置都不一样,这里需要用到随机数。我们可以用
srand((unsigned)time(NULL));
产生一个随机数种子,再用
rand() % (end-start + 1) + start;
在范围end~start间(包括二者)产生一个随机的数字。
为了不让用户在第一次输入时候就踩到雷,我们可以在用户输入第一个坐标后再进行埋雷,此时只要在判断条件中去除第一次坐标就可以了user_x&&y!=user_y
完整代码如下:

/*埋雷*/
void setmine(char Mine[][COLS],int _rows,int _cols,int user_x,int user_y)		
{
	int x, y;
	srand((unsigned)time(NULL));		//随机数种子
 	for (int i = 0; i < MINE_COUNT;){
		x = GetInex(1, _rows - 2);		//获得随机坐标
		y = GetInex(1, _cols - 2);
		if (Mine[x][y] == '0'&&x!=user_x&&y!=user_y){
			Mine[x][y] = '1';			//埋雷
			i++;
		}}}

为什么rows和cols要-2呢?我们会在下面说到。

/*获得随机坐标*/
	int GetInex(int start,int end){		
	int index;
	index=rand() % (end-start + 1) + start;
	return index;
	}

3.显示面板
  有了雷和用户面板的信息,接下来我么就需要把它打印到屏幕上来。同时也将横纵坐标的轴给打印出来,方便用户输入坐标。
打印的方式多种多样,笔者在这用的最简单的面板,各位可自行设计。
打印出来的结果:
在这里插入图片描述
代码:

/*显示面板*/
 void showbroad(char show[][COLS], int _rows, int _cols)		
{
	printf("   ");
	for (int i = 1; i < _cols-1; i++){
		printf("%d ", i);
	}
	printf("\n  ");
	for (int i = 1; i<_cols-1; i++){
		printf("__");
	}
	for (int i = 1; i<_rows - 1; i++){
		printf("\n%2d|",i);
		for (int j = 1; j < _cols - 1; j++){
			printf("%c ",show[i][j]);
		}
	}
	printf("\n");
}

3.游戏开始

埋好了雷,有了面板,接下来便要开始游戏的逻辑编写了。

  1. 游戏开始时,我们首先看到的是一个有坐标的面板,这时直接调用上面的显示面板函数,传入show数组,行和列:

showbroad(show, ROWS, COLS);
接下来提示用户输入坐标并埋雷:

		 printf("Please Enter <rows,cols>:\n");
		 scanf("%d%d", &x, &y); 
		 if (i){
			 setmine(mine, ROWS, COLS,x,y);								//埋雷
			 i--;
		 }
  1. 判断用户输入是否合法,不合法则提示重新输入
if ((x >= 1 && x <= _row - 2) && (y >= 1 && y <= _cols)){...}
else printf("Enter Erro Try Again:");
  1. 当输入合法时候,就要开始判断用户是否踩到了雷,没有则继续,否则显示Game over,退出。
if (mine[x][y] == '0'){...}
else {printf("Game Over!\n");
				 break; }

当用户没踩到雷时候,将该位置翻开,显示为周围(8个)位置雷数的总和,并将排雷数+1.

int num = GetNearMine(mine,x,y);	//统计周围雷个数
 show[x][y] = num+'0';				//填充数字
 (*p)++;
 /*统计周围雷数*/
 int GetNearMine(char mine[][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';
 }

此时我们发现当地雷所在位置是面板的边上时,统计雷数时候有那么几个位置是超出了面板范围不能统计的,为了解决这个问题,我们在设置面板时候将面板的行和列都+2。(ROWS+2)*(COLS+2),但显示时候只显示行列-2的面板(ROWS×COLS)。这样在统计雷数时候就不会出错了,因为那些超出的部分都已经被我们设置为了‘0’。
  所以上面为什么是rows-2和cols-2的问题也清楚了。

  1. 逻辑进行到这一步,接下来我们就需要对那些统计雷数为0的坐标进行展开了。
		 if (num == 0){											
		 Expand(mine, show, x, y,p); //展开
		}

要展开我们需要将该为0的坐标点周围的8个坐标依次进行与上面同样的个数统计流程,这里我们使用一个switch语句来获得周围8个点的坐标,并用一条if语句设置坐标检查,统计每个坐标周围的总雷数,并将排雷点数+1。当那些坐标也符合总雷数为0的条件时,再次调用函数递归。
代码:

 /*展开*/
void Expand(char mine[][COLS],char show[][COLS],int user_x,int user_y,int *p)
 {
	 int x;
	 int y;
	 for (int i = 0; i < 8; i++){	//以输入坐标为中心逐个判断
		 x = user_x;
		 y = user_y;
		 
		 switch (i){
		 case(0) : x--; y--; break;
		 case(1) : x--;		 break;
		 case(2) : x--; y++; break;
		 case(3) : y--;		 break;
		 case(4) : y++;		 break;
		 case(5) : x++; y--; break;
		 case(6) : x++;		 break;
		 case(7) : x++; y++; break;
		 }
		 if (x<1 || x>10 || y<1 || y>10||show[x][y]!='*'){ continue; }		//坐标越界或被排查过则跳过

		 int _num = GetNearMine(mine, x, y);
		 show[x][y] = _num + '0';
		 (*p)++;

		 if (_num == 0){
			 Expand(mine, show, x, y,p);				//递归
		 }
	 }
 }

最后我们将坐标刷新,并用已排雷数判断是否已排完

showbroad(show, ROWS, COLS);
//showbroad(mine, ROWS, COLS);
if ((ROWS - 2)*(COLS - 2) - *p == MINE_COUNT){	//排完所有雷
 printf("YOU WIN! :)\n");

接下来,我们将这些代码编入循环,让游戏可以一直进行下去,直到胜利或失败。
代码:

/*游戏逻辑*/
 void Play(char mine[][COLS], char show[][COLS], int _row, int _cols){
	 int x, y, i = 1;
	 showbroad(show, ROWS, COLS);
	 int count = 0;
	 int *p = &count;
	 while (1)
	 {
		 printf("Please Enter <rows,cols>:\n");
		 scanf("%d%d", &x, &y); 
		 if (i){
			 setmine(mine, ROWS, COLS,x,y);					//埋雷
			 i--;
			 //showbroad(mine, ROWS, COLS);
		 }
		 if ((x >= 1 && x <= _row - 2) && (y >= 1 && y <= _cols-2))		
		 //检查输入是否合法
		 {		
			 if (mine[x][y] == '0')									//没有踩雷
			 {									
				 int num = GetNearMine(mine,x,y);			//统计周围雷个数
				 show[x][y] = num+'0';
				 (*p)++;
				 if (num == 0){											
					 Expand(mine, show, x, y,p);			//展开
				 }
				 system("cls");										//刷新
				 showbroad(show, ROWS, COLS);
				 //showbroad(mine, ROWS, COLS);
				 if ((ROWS - 2)*(COLS - 2) - *p == MINE_COUNT){		
				 											//排完所有雷
					 printf("YOU WIN! :)\n");
					 break;
				 }
			 }
			 else {printf("Game Over!\n");
				 break;
			 }
		 }
		 else printf("Enter Erro Try Again:");
	 } }

至此,我们便用c语言实现了一个简单的扫雷。
如下是游戏界面:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值