扫雷(C实现)

        首先,我们对扫雷这个项目进行划分,划分为几个模块:主界面、游戏界面(棋盘)、结算界面。然后对这几个模块再进行划分,划分为几个函数用来实现此部分的功能。我们对游戏的逻辑进行分析:

1. 展示主界面

2. 玩家选择玩游戏或者退出

(分支)3.1. 进行游戏 / 3.2 退出。

由上面这个逻辑结构我们可以写出主函数的代码:

using namespace std;
int main() {
	int choice = 0;
	do {
		menu();
		cin >> choice;
		switch (choice)
		{
		case 1:
			game(); 
			break;
		case 2:
			cout << "退出游戏" << endl; 
			break;
		default:
			cout << "选择错误,重新选择" << endl;
			break;
		}
	} while (choice);
	
	return 0 ^ 0;
}

        我们将主界面封装为一个函数,用来向玩家展示信息,然后让玩家选择:

void menu() {
	printf("*************************\n");
	printf("*******   1.play   ******\n");
	printf("*******   0.exit   ******\n");
	printf("*************************\n");
	
}

        将游戏内部的逻辑封装在一个函数里并在主函数中进行调用,即game(),然后我们对游戏内部的逻辑进行分析

1.创建棋盘并初始化

2.随机布置雷

3.打印一个初始棋盘

4.玩家选择排雷的坐标

(分支)5.1不是雷,返回3  /  5.2是雷,寄了。

(判断)5.1.1雷全部排完,胜利

由以上逻辑:

void game() {
	srand((unsigned int)time(NULL));//为rand提供种子
	int num = EZ_MINE;//更改游戏难度
	char mine[ROWS][COLS] = { 0 };//棋盘mine用于存放雷的坐标信息
    //ROWS=ROW+2;COLS=COL+2;这是为了防止检查某坐标的附近坐标时数组越界访问
	init_board(mine, '0');
	setMine(mine, num);//随机布置雷
	char show[ROWS][COLS] = { 0 };//棋盘show用于向玩家展示
	init_board(show, '*');//初始化棋盘
	
	while (win) {//win是一个全局变量,定义:int win = ROW * COL - EZ_MINE;
//win用于记录非雷并且未排查的坐标数目
		int x = 0, y = 0;
	//	printBoard(mine);//用于测试
		printBoard(show);//打印棋盘
		printf( "请输入要排查的坐标:");
		scanf("%d%d", &x, &y);
		if (mine[x][y] != '1') {//判断此坐标是否已经排查
			if (show[x][y] != '*') {
				printf("已经排查过了\n");
				continue;
			}
			check(mine, show,x, y);	//判断是不是雷		
			
			
		}
		else {
			
			printBoard(mine);
			printf("你寄了\n");
			break;
		}
	
	}
	if (win == 0){
        printBoard(mine);
        printf("你赢了\n");
    }
}

        然后我们再对game函数的子函数进行逻辑分析,首先是初始化棋盘init_board(),我们的目标是把每个坐标全都初始化为一个指定字符,易得出:

void init_board(char mine[][COLS], char fill) {
	for (int i = 0; i < ROWS; i++) {
		for (int j = 0; j < COLS; j++) {
			mine[i][j] = fill;
		}
	}
}

        然后是setMine,随机产生多个不重复坐标,并把mine中这几个坐标的值设为1:

void setMine(char mine[][COLS], int num) {
	int cnt = 0;
	while (cnt<num) {
		int x = rand() % ROW + 1;
		int y = rand() % COL + 1;
		if (mine[x][y] != '1') { mine[x][y] = '1'; cnt++; }
		else continue;
	}
}

        再是printBoard,只需遍历然后打印即可:

void printBoard(char show[][COLS]) {
	printf("--------------------------------\n");
	for (int j = 0; j <= COL; j++)printf("%d |", j);//方便玩家获得横坐标
	printf("\n");
	for (int i = 1; i <= ROW; i++) {
		printf("%d |", i);//方便玩家获得纵坐标
		for (int j = 1; j <= COL; j++) {
			printf("%c  ", show[i][j]);
		}
		printf("\n");
	}
	printf("--------------------------------\n");
}

        最后是重头戏check,他应该接收一个坐标然后返回这个坐标附近8个坐标的雷的数目,然后把show中此坐标的值设为返回值。这是一种简单的实现方法,但是如果我们想要实现游戏中一点出现一片的效果,单是这样是不够的,我们需要再进行分析:

        如果这个坐标不是雷,并且它附近的坐标没有被检查过,并且这个附近坐标的值应为0,我们就对这个附近坐标再进行check操作,以此类推,(类似于DFS)直到这片check区域的边缘的坐标值大于0:

void check(char mine[][COLS], char show[][COLS], int x, int y) {
	int cnt= (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');
	show[x][y] = cnt+'0';
	win--;
	if (cnt == 0 && show[x - 1][y - 1] == '*'&&mine[x-1][y-1]!='1'&&x-1>=1&&x-1<=ROW&&y-1>=1&&y-1<=COL)check(mine, show, x - 1, y - 1);
	if (cnt == 0 && show[x - 1][y ] == '*' && mine[x - 1][y ] != '1' && x-1 >= 1 && x-1 <= ROW && y >= 1 && y <= COL)check(mine, show, x - 1, y );
	if (cnt == 0 && show[x - 1][y + 1] == '*' && mine[x - 1][y + 1] != '1' && x-1 >= 1 && x-1 <= ROW && y+1 >= 1 && y+1 <= COL)check(mine, show, x - 1, y + 1);
	if (cnt == 0 && show[x ][y - 1] == '*' && mine[x ][y - 1] != '1' && x >= 1 && x <= ROW && y-1 >= 1 && y-1 <= COL)check(mine, show, x , y - 1);
	if (cnt == 0 && show[x ][y + 1] == '*' && mine[x ][y + 1] != '1' && x >= 1 && x <= ROW && y+1 >= 1 && y+1 <= COL)check(mine, show, x , y + 1);
	if (cnt == 0 && show[x + 1][y - 1] == '*' && mine[x + 1][y - 1] != '1' && x+1 >= 1 && x+1 <= ROW && y-1 >= 1 && y-1 <= COL)check(mine, show, x +1, y - 1);
	if (cnt == 0 && show[x + 1][y ] == '*' && mine[x + 1][y ] != '1' && x+1 >= 1 && x+1 <= ROW && y >= 1 && y <= COL)check(mine, show, x +1, y );
	if (cnt == 0 && show[x +1][y + 1] == '*' && mine[x + 1][y + 1] != '1' && x+1 >= 1 && x+1 <= ROW && y+1 >= 1 && y+1 <= COL)check(mine, show, x +1, y + 1);
	return;
}

        然后再包含一下头文件,就大功告成了!

 图1.check效果

 图2.游戏效果(EZ_MINE=3)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值