C语言:数组的应用2——扫雷(递归实现地图变化)

之前呢跟大家分享了二维数组实现的小游戏——三子棋(井字棋),大家都看懂了吗?今天给大家分享一下用数组实现的扫雷小游戏。先看看最终的效果吧。

 我设计的这个扫雷游戏,可以让玩家自己选择游戏难度,有简单,适中,困难三种模式。并利用递归的方式去改变地图,使得在视觉上看着跟真正的扫雷游戏相近。接下来我们一步一步的实现游戏的功能。

游戏开始

首先我们得让玩家知道怎么样去开始游戏,所以我们得先打印一个游戏头,让玩家自己选择是开始游戏还退出游戏。我们把打印游戏头得功能封装到一个函数里面。

// 打印游戏头
void game_head() {
	printf("######################\n");
	printf("#        1.PLAY      #\n");
	printf("#        0.EXIT      #\n");
	printf("######################\n");
}

有了游戏头之后就是实现我们游戏的主体逻辑了,就是输入1就开始游戏,输入0就结束游戏。那假如玩家在玩了一把之后还想玩怎么办呢?所以我们的 主要框架得放到一个循环中,所以代码如下

int main(){
    int input = 0;
    do{
        game_head(); // 上面提到的打印游戏头的函数
        printf("请输入1开始游戏>>");
        scanf("%d", &input);
        switch(input){
            case 1:
                printf("开始扫雷\n");
                break;
            case 0:
                printf("结束游戏\n");
                break;
            default:
                printf("输入错误,请重新输入\n");
                break;
        }
    }while(input);


    return 0;
}

 有了上面这个框架我们只需要把那些共能函数和一些其他的细节逐渐完善就好了。

我们在文章开头讲了,这个扫雷游戏是可以选择难度的。所以当玩家选择开始游戏之后,就是让玩家选择想玩的游戏难度。再想一想,这个游戏难度是不是跟游戏棋盘的大小有关呢?简单的是9*9的棋盘,适中的是16*16的棋盘,困难的是16*30的棋盘。而且不同难度的地雷数是不一样的。所以玩家选择的难易程度是和后面很多都有关联的,所以用来存放难易程度值得变量得设计成一个全局变量。

在选择了难度之后,我们就得初始化地图了。如果,我们这里采用的使用两个二维数组来完成我们的地图,一个是呈现给玩家看的地图,第二个数组就用来处理 玩游戏时的数据,比如地雷的位置放到第二个数组中,那么具体要怎么初始化呢?先看看代码:

int in = 0; //全局变量
#define ES 9
#define MD 16
#define HI 30
// 初始化数组
void init(int*** data, char*** map) {
	int i = 0;
	printf("==================\n");
	printf("      1.EASY      \n");
	printf("      2.MEZZO      \n");
	printf("      3.DIFF      \n");
	printf("==================\n");
	printf("请选择游戏难度>>");
	scanf("%d", &in);
	switch (in) {
	case EASY:
		*data = (int**)malloc(sizeof(int*) * (ES+2));
		*map = (char**)malloc(sizeof(char*) * (ES + 2));
		for (i = 0; i < ES + 2; i++) {
			(*data)[i] = (int*)malloc(sizeof(int) * (ES+2));
			(*map)[i] = (int*)malloc(sizeof(int) * (ES + 2));
		}
		for (i = 0; i < ES + 2; i++) {
			int j = 0;
			for (j = 0; j < ES + 2; j++) {
				(*data)[i][j] = 0;
				(*map)[i][j] = '*';
			}
		}
		break;
	case MEZZO:
		*data = (int**)malloc(sizeof(int*) * (MD + 2));
		*map = (char**)malloc(sizeof(char*) * (MD + 2));
		for (i = 0; i < MD + 2; i++) {
			(*data)[i] = (int*)malloc(sizeof(int) * (MD + 2));
			(*map)[i] = (int*)malloc(sizeof(int) * (MD + 2));
		}
		for (i = 0; i < MD + 2; i++) {
			int j = 0;
			for (j = 0; j < MD + 2; j++) {
				(*data)[i][j] = 0;
				(*map)[i][j] = '*';
			}
		}
		break;
	case DIFF:
		*data = (int**)malloc(sizeof(int*) * (MD + 2) );
		*map = (char**)malloc(sizeof(char*) * (MD + 2));
		for (i = 0; i < MD + 2; i++) {
			(*data)[i] = (int*)malloc(sizeof(int) * (HI + 2));
			(*map)[i] = (int*)malloc(sizeof(int) * (HI + 2));
		}
		for (i = 0; i < MD + 2; i++) {
			int j = 0;
			for (j = 0; j < HI + 2; j++) {
				(*data)[i][j] = 0;
				(*map)[i][j] = '*';
			}
		}
		break;
	default:
		break;
	}
}

当我们选择 不同的难度时,我们初始化的地图大小也是不一样的。所以在程序中我们使用了switch语句,针对不同的难度,初始化不同大小的数组。细心的朋友应该可以发现,比如我9*9的地图,初始化的二维数组大小是11*11的,这是为什么呢?这就跟扫雷游戏的原理有关了。我们在玩扫雷的时候,只要点一个格子,那个位置就会出现一个数字。这个数字是怎么来的呢,其实那就是那个格子周围一圈雷的数量。那么,如果玩家点击了地图边缘的格子,我们在判断的时候是不是就越界了呢。所以我们把数组创建大一圈,就不会越界了。

初始化地图之后就是打印我们的游戏地图了,就是把我们初始化的第一个数组打印出来就好了。同样的,不同的难度打印的地图也是不一样的。具体就看看代码吧

void DispMap(char** map) {
	int i = 0;
	int j = 0;
	system("CLS");
	switch (in) {  // in是全局变量,所以在这里可以直接用
	case EASY:
		for (i = 1; i < ES + 1; i++) {
			if (i == 1) {
				printf(" |%d", i);
			}
			else
			{
				printf(" %d", i);
			}

		}
		printf("\n");
		for (i = 1; i < ES+1; i++) {
			printf("%d|", i);
			for (j = 1; j < ES+1; j++) {
				printf("%c ", map[i][j]);
			}
			printf("\n");
		}
		break;
	case MEZZO:
		for (i = 1; i < MD + 1; i++) {
			if (i == 1) {
				printf("  |%-2d", i);
			}
			else
			{
				printf(" %-2d", i);
			}

		}
		printf("\n");
		for (i = 1; i < MD+1; i++) {
			if (i < 10)
				printf("%d |", i);
			else
				printf("%d|", i);
			for (j = 1; j < MD+1; j++) {
				printf("%c  ", map[i][j]);
			}
			printf("\n");
		}
		break;
	case DIFF:
		for (i = 1; i < HI + 1; i++) {
			if (i == 1) {
				printf("  |%-2d", i);
			}
			else
			{
				printf(" %-2d", i);
			}

		}
		printf("\n");
		for (i = 1; i < MD+1; i++) {
			if (i < 10)
				printf("%d |", i);
			else
				printf("%d|", i);
			for (j = 1; j < HI+1; j++) {
				printf("%c  ", map[i][j]);
			}
			printf("\n");
		}
		break;
	}
	
}

接着呢就是来布置地雷,在初始化地图的时候我们把data数组初始化为全0的数组。所以在布置地雷的时候,把地雷所在的位置改成1。这样,在玩家输入一个坐标后,如果在data数组中对应坐标的那个值是1的话,就说明踩到地雷了。代码如下:

// 放置地雷
void SetMine(int*** data) {
	int i = 0;
	int x = 0;
	int y = 0;
	srand((unsigned int)time(NULL));
	switch (in) {
	case EASY:
		i = 0;
		while (i < 10) {
			x = rand() % ES + 1;
			y = rand() % ES + 1;
			if ((*data)[x][y] == 0) {
				(*data)[x][y] = 1;
				i++;
			}
		}
		break;
	case MEZZO:
		i = 0;
		while (i < 30) {
			x = rand() % MD + 1;
			y = rand() % MD + 1;
			if ((*data)[x][y] == 0) {
				(*data)[x][y] = 1;
				i++;
			}
		}
		break;
	case DIFF:
		i = 0;
		while (i < 54) {
			x = rand() % MD + 1;
			y = rand() % HI + 1;
			if ((*data)[x][y] == 0) {
				(*data)[x][y] = 1;
				i++;
			}
		}
		break;
	}
}

到这一步,地雷和地图都已经布置好啦。接下来就是玩家玩游戏了,当玩家输入一个坐标(x,y)后,如果这个坐标没有地雷,我们就得计算一下它的周围一共有多少颗雷。上代码

int play_game(int** data, int x, int y) {
	int count = 0;
	if (data[x][y] == 1) {
		return 9;
	}
	else {
		if (data[x][y - 1] == 1) count++;
		if (data[x - 1][y - 1] == 1) count++;
		if (data[x - 1][y] == 1) count++;
		if (data[x - 1][y + 1] == 1) count++;
		if (data[x][y + 1] == 1) count++;
		if (data[x + 1][y + 1] == 1) count++;
		if (data[x + 1][y] == 1) count++;
		if (data[x + 1][y - 1] == 1) count++;
	}
	return count;
}

可能你又会问了“这个判断出来了怎么在地图上体现出来呢?”,所以,下面的一个函数来啦,这个函数就是改变地图。比如说玩家输入一个坐标,这个坐标的周围有3颗地雷,那么打印出来的时候,坐标位置就不是" * ",而是“ 3 ”。如果这个坐标的 周围一个雷也没有,那么这个坐标打印出来的时候就是‘ ’(空格)。然后用递归的方式,把一片雷数为0的左边变成空格。看代码:

// 更改地图
void change_map(char*** map, int** data, int x, int y) {
	int ret = 0;
	switch (in) {
	case EASY:
		if ((x <= ES && x >0) && (y <= ES && y >0)) {
			ret = play_game(data, x, y);
			if (ret != 0) {
				(*map)[x][y] = ret + 48;
				win++;
			}
			else {
				(*map)[x][y] = ' ';
				win++;
                // 如果坐标的周围一颗雷也没有  向坐标的上、下、左、 右四个方向递归
				if ((*map)[x][y - 1] == '*')  // 坐标的位置不能等于‘ * ’,是防止重复递归  
					change_map(map, data, x, y - 1);
				if ((*map)[x][y + 1] == '*')
					change_map(map, data, x, y + 1);
				if ((*map)[x - 1][y] == '*')
					change_map(map, data, x - 1, y);
				if ((*map)[x + 1][y] == '*')
					change_map(map, data, x + 1, y);
			}
		}
		break;
	case MEZZO:
		if ((x <= MD && x > 0) && (y <= MD && y > 0)) {
			ret = play_game(data, x, y);
			if (ret != 0) {
				(*map)[x][y] = ret + 48;
				win++;
			}
			else {
				(*map)[x][y] = ' ';
				win++;
				if ((*map)[x][y - 1] == '*')
					change_map(map, data, x, y - 1);
				if ((*map)[x][y + 1] == '*')
					change_map(map, data, x, y + 1);
				if ((*map)[x - 1][y] == '*')
					change_map(map, data, x - 1, y);
				if ((*map)[x + 1][y] == '*')
					change_map(map, data, x + 1, y);
			}
		}
		break;
	case DIFF:
		if ((x <= MD && x > 0) && (y <= HI && y > 0)) {
			ret = play_game(data, x, y);
			if (ret != 0) {
				(*map)[x][y] = ret + 48;
				win++;
			}
			else {
				(*map)[x][y] = ' ';
				win++;
				if ((*map)[x][y - 1] == '*')
					change_map(map, data, x, y - 1);
				if ((*map)[x][y + 1] == '*')
					change_map(map, data, x, y + 1);
				if ((*map)[x - 1][y] == '*')
					change_map(map, data, x - 1, y);
				if ((*map)[x + 1][y] == '*')
					change_map(map, data, x + 1, y);
			}
		}
		break;
	}
}

判断输赢

int is_win() {
	switch (in) {
	case EASY:
		if (win == ES*ES-10)  // win是一个全局变量
			return 1;
		break;
	case MEZZO:
		if (win == MD*MD-30)
			return 1;
		break;
	case DIFF:
		if (win == HI*HI-54);
			return 1;
		break;
	}
	return 0;
}

最后我们再把游戏的框架再完善一下:

#include"minesweeping.h"

int main() {
	int input = 0;
	int** data = NULL;
	char** map = NULL;
	int ret = 0;
	do {
		game_head();
		printf("输入1开始游戏>>");
		scanf("%d", &input);
		switch (input) {
		case PLAY:
			init(&data, &map);
			SetMine(&data);
			DispMap(map);
			int x = 0;
			int y = 0;
			
			while (1) {
				if (is_win() == 1) {
					printf("你赢了\n");
					break;
				}
				printf("请输入要查看的坐标>>");
				scanf("%d%d", &x, &y);
				ret = play_game(data, x, y);
				if (ret == 9) {
					printf("很遗憾,踩雷了\n");
					break;
				}
				else {
					change_map(&map, data, x, y);
				}
				DispMap(map);
			}
			break;
		case EXIT:
			printf("游戏结束\n");
			free(data);
			free(map);
			break;
		default:
			break;
		}
	} while (input != 0);
	return 0;
}

完整代码:https://gitee.com/damiing/game

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_yiyi_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值