【扫雷游戏】(代码通俗易懂+递归优化及实现)


前言

本系列博文仅为博主个人学习笔记,通过博客理清学习思路用于复习。如有记述不周到的地方请谅解;如能指出,更加感谢。


一、前期准备–游戏介绍

      扫雷游戏它是由row * col的格子拼起来,游戏规则:这款游戏的玩法是在一个99(初级),1616(中级),16*30(高级),或自定义大小的方块矩阵中随机布置一定量的地雷(初级为10个,中级为40个,高级为99个)。由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。如果玩家翻开的方块有地雷,则游戏结束。
扫雷

// 本程序采用两个二维数组存储棋盘
	char mine[row][col] ; // 用来存储雷的信息
	char show[row][col] ; // 用来展示当前玩家展开棋盘的信息
```![扫雷](https://img-blog.csdnimg.cn/20210429214450977.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMjQ5NzYw,size_16,color_FFFFFF,t_70)
## 1.该如何用c语言实现这样一款游戏 ---- 代码分析
    该游戏主要步骤就是两步1.如何设置雷2.如何排查雷。

1. 设置雷

```c
	//随机产生一对坐标,存储在x ,y当中
	int x = rand() % row + 1 ;
	int y = rand() % col + 1 ;
	//若存储地雷的二维数组该坐标不是雷,则将其设置成雷
	if(mine[x][y] == '0'){
		mine[x][y] = '1';
	}

2、排查雷
      玩家输入一对坐标,对坐标进行判断是否是合法输入,如果是合法输入,则继续判断该坐标对应的mine数组是否为雷,若为雷,则结束游戏,提示玩家您不小心踩到啦雷,游戏结束;若没有踩到雷,则继续游戏,并将该格子对应坐标周围所有的雷数展示出来,玩家继续挖雷,直到所有的不是雷的格子被玩家全部挖出,则游戏结束,提示玩家获胜。

playerMove(char mine[][COLS],char show[][COLS], int row, int col) {

 int x = 0;
 int y = 0;
 int count = 0;

 while (count < row * col - NUM_MINE) {
 	printf("请输入您要排除的雷的坐标:");
 	scanf("%d %d", &x, &y);
 	if (x >= 1 && x <= row && y >= 1 && y <= col) {
 		if (mine[x][y] == '1') {
 			printf("不好意思,你被炸啦,游戏结束\n");
 			break;
 		}
 	    /*int ret   = count_mine(mine, x, y);
 		show[x][y] = ret + '0';
 		count++;*/
 		else {
 		//对代码的递归优化,详细请见下文,功能是将与输入坐标周围8个格子所有不是雷的展开,提高用户游戏体验。
 			OpenMine(mine, show, row, col, x, y, &count);
 		//打印当前游戏棋盘的状态
 			Display(show, row, col);
 		}
 	}
 	else {
 		printf("输入坐标非法:\n");
 	}
 }
 if (count >= (col * row - NUM_MINE)) {
 	printf("恭喜您,扫雷成功\n");
 }
}

二、代码的具体实现

本程序分为三个文件 game.h 头文件,game.c 具体实现文件,test.c测试文件 。

1. game.c 头文件

#include <stdio.h>

#define NUM_MINE 10
#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

//initial array 1. mine 2. show
initial(char board[][COLS], int rows, int col,char set);

//display 1. mine 2. show
Display(char board[][COLS], int row, int col);

//set mine
set_mine(char mine[][COLS], int row, int col);

//remove mine 
playerMove(char mine[][COLS],char show[][COLS], int row, int col);

2. game.c具体实现文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

//initial array 1. mine 2. show
initial(char board[][COLS], int rows, int cols,char set) {
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}

	}
}

//display 1. mine 2. show
Display(char board[][COLS], int row, int col) {
	printf("------扫雷游戏------\n");
	for (int i = 0; i <= row; i++) {
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("------扫雷游戏------\n");


}

//set mine
set_mine(char board[][COLS], int row, int col) {
	int count = NUM_MINE;

	while (count) {
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0') {
			board[x][y] = '1';
			count--;
		}
	}
}
//count mine by every grid
static int count_mine(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] - '0' * 8;
}

//open mines one by one
static void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y,int *level)
{
	(*level)++;
	if (mine[x][y] == '1' || x < 1  || x > row || y < 1 || y > col || show[x][y] != '*') {
		(*level)--;
		return;
	}
	int ret = count_mine(mine, x, y); //调用统计雷个数的函数。
	if (!ret)
	{
		show[x][y] = ' ';
		//要确定周围8个坐标本身不是雷,才递归它周围的。
		OpenMine(mine, show, row, col, x - 1, y - 1,level);
		OpenMine(mine, show, row, col, x, y - 1,level);
		OpenMine(mine, show, row, col, x + 1, y - 1, level);
		OpenMine(mine, show, row, col, x - 1, y, level);
		OpenMine(mine, show, row, col, x + 1, y, level);
		OpenMine(mine, show, row, col, x - 1, y + 1, level);
		OpenMine(mine, show, row, col, x, y + 1, level);
		OpenMine(mine, show, row, col, x + 1, y + 1, level);
	}
	else
	{
		show[x][y] = ret + '0'; //显示该坐标周围有几个雷
		return;
	}

	
}

//playermoving
playerMove(char mine[][COLS],char show[][COLS], int row, int col) {

	int x = 0;
	int y = 0;
	int count = 0;

	while (count < row * col - NUM_MINE) {
		printf("请输入您要排除的雷的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col) {
			if (mine[x][y] == '1') {
				printf("不好意思,你被炸啦,游戏结束\n");
				break;
			}
		    /*int ret   = count_mine(mine, x, y);
			show[x][y] = ret + '0';
			count++;*/
			else {
				OpenMine(mine, show, row, col, x, y, &count);
				Display(show, row, col);
			}
			
		}
		else {
			printf("输入坐标非法:\n");
		}
	}

	if (count >= (col * row - NUM_MINE)) {
		printf("恭喜您,扫雷成功\n");
	}

}

3. test.c 测试文件

#include "game.h"
#include <stdlib.h>
#include <time.h>
void menu() {
	printf("*********************\n");
	printf("******1. play   *****\n");
	printf("******0. exit   *****\n");
	printf("*********************\n");
}
void game() {
	//定义两数组来存储数据
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };

	//initial array
	initial(mine, ROWS, COLS,'0');
	initial(show, ROWS, COLS,'*');
	//Display(mine, ROW, COL);
	Display(show, ROW, COL);

	//set mine 
	set_mine(mine, ROW, COL);
	playerMove(mine,show, ROW, COL);
	Display(mine, ROW, COL);


}
int main() {

	srand((unsigned)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择:> ");
		scanf("%d",&input);
		switch (input) {
		case 1:
			game();
			break;
		case 0:
			printf("退出\n");
			break;
		default :
			printf("输入错误\n");
			break;
		}

	} while (input);

	return 0;
}

      此处重点来说说openmine 这个函数,功能是一个一个的展开周围的8个格子,若该格子的坐标不合法,曾访问过或者是雷,则满足条件,return返回,若满足以上条件均不满足,则将该格子周围的雷数进行统计,若统计数不为零,则不将其周围格子进行展开,只将其格子周围的雷数展示出来;若统计数为0,则将该格子置为‘   ’,并进入下一个格子 的判断:将该格子的左上,左,左下,正上,正下,右上,右,右下8个格子继续进行以上三个判断,并用一个变量count计数,依次判断,直到所有相邻的格子判断完毕。若count < row * col - 雷数, 则玩家继续输入坐标进行下一轮判断。重复上述动作,在玩家没有踩到雷的情况下正常,直到 count < row* col - 雷数不满足条件,跳出循环 ,提示玩家胜利。

                                            游戏界面展示
在这里插入图片描述

总结:

      扫雷游戏还是比较简单的,希望同学们一起努力,从c语言青铜慢慢变成c语言王者,我们一起在山顶相遇。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值