扫雷-C语言-进阶(递归自动展开+棋子标记)

最终效果图(部分)

上方先感慨一下:实属不易,研究了较长时间最终成功了= =

test.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include"扫雷.h"

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

// 安排两个二维数组,一个储存雷的信息,一个储存排查的信息
void game()
{
	int number = 0;
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	Init_operation(mine, ROWS, COLS, '0');
	Init_operation(show, ROWS, COLS, '*');
	set_mine(mine, ROW, COL);
	print_board(mine, ROW, COL);

	print_board(show, ROW, COL);

	check_mine(mine, show, ROW, COL);


	//print_show(mine, ROW, COL);

	//while (1)
	//{
	//	int ret = check_mine(mine, show, ROW, COL, number);
	//	if (!ret)
	//	{
	//		printf("游戏失败,扫雷失败\n");
	//		print_show(mine, ROW, COL);
	//		printf("游戏失败,扫雷失败\n");
	//		break;
	//	}
	//	if (number == (ROW - 2) * (COL - 2) - MINE)
	//	{
	//		printf("游戏胜利,扫雷成功\n");
	//		print_show(mine, ROW, COL);
	//		printf("游戏胜利,扫雷成功\n");
	//		break;
	//	}
	//	print_show(show, ROW, COL);
	//}
}

int main()
{
	srand((unsigned int)time(NULL));
	int ret = 0;
	do
	{
		menu();
		printf("请输入选项:\n");
		scanf("%d", &ret);
		switch (ret)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出成功\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (ret);
	return 0;
}

扫雷.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<ctime>
#include<stdlib.h>
#include<iostream>
using namespace std;
//#define ROW 11
//#define COL 11
#define ROW 9
#define COL 9

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

#define MINE 9
// 初始化操作
void Init_operation(char arr[ROWS][COLS], int row, int col, char flag);

// 布置雷
void set_mine(char arr[ROWS][COLS], int row, int col);

// 打印雷盘
void print_board(char arr[ROWS][COLS], int row, int col);

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

扫雷.cpp

Init_operation

void Init_operation(char arr[ROWS][COLS], int rows, int cols, char flag)
{
	// 初始化操作,雷盘初始化为0,打印初始化为*
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			arr[i][j] = flag;
		}
	}
}

用于初始化棋盘,mine棋盘初始化为0(后面布置雷为1) ,show初始化为*。注意此处为cols,rows。后面的是row,col。

set_mine 

void set_mine(char arr[ROWS][COLS], int row, int col)
{
	int num = MINE;
	while (num)
	{
		int x = rand() % row + 1; //1-9
		int y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			num--;
		}
	}
}

布置雷

print_board

void print_board(char arr[ROWS][COLS], int row, int col)
{
	for (int i = 0; i <= col; 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 ", arr[i][j]);
		}
		printf("\n");
	}
}

打印棋盘,里面用的是row,col,而不是一些常量

number_of_mine

int number_of_mine(char mine[ROWS][COLS], int x, int y)
{
	// 我改的是show数组  标记的时候改为# 一旦改正成功 ,则mine数组中相应位置的1应该改为0
	int ret = 0;
	for (int i = x - 1; i <= x + 1; i++)
	{
		for (int j = y - 1; j <= y + 1; j++)
		{
			ret += mine[i][j] - '0';
		}
	}
	return ret;
}

统计非雷坐标周围的雷的个数

check_pro

void check_pro(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	int flag = 1;
	if (show[x][y] == '*'/* && mine[x][y] != '1'*/)  //没有被排查过
	{
		flag = 0;  //0则没有被排查过
	}
	int ret = number_of_mine(mine, x, y);
	if (ret == 0)
		show[x][y] = ' ';
	else
		show[x][y] = ret + '0';
	if(flag == 0 && ret == 0)
		for (int i = x - 1; i <= x + 1; i++)
		{
			for (int j = y - 1; j <= y + 1; j++)
			{
				//周围的可以进一步展开的条件是:1. 它周围没有雷  2.它是*
				check_pro(mine, show, i, j);
			}
		}
}

用于递归展开

check_mine

void check_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	//排查雷的功能有:1. 选择某个地方排查,  2. 标记棋子  3. 在排查的时候要递归展开一片
	int tag = 0;  //旗子个数 
	int count = 0;//排查出的个数
	int input = 0;
	int x = 0, y = 0;
	while (tag != MINE && count != row * col - MINE)
	{
		count = 0;
		printf("请选择你要进行的操作:\n");
		printf("1.check  2.tag\n");
		cin >> input;
		printf("请输入要操作的位置:\n");
		cin >> x >> y;
		if ((input == 1 || input == 2) && x >= 1 && x <= row && y >= 1 && y <= col)
		{
			switch (input)
			{
			case 1:
				if (mine[x][y] != '1')  //不是雷
				{
					int num = number_of_mine(mine, x, y);
					//这里的操作是:雷区确实不是雷且没排查过
					if (num == 0)
					{
						check_pro(mine, show, x, y);
						print_board(show, row, col);    //展开后,打印出show
					}
					else
					{
						show[x][y] = num + '0';
						print_board(show, row, col);
					}
				}
				else
				{
					printf("排查失败,%d,%d是雷\n", x, y);
					printf("扫雷失败\n");
					print_board(mine, row, col);
					return;
				}
				break;
			case 2:
				if (mine[x][y] == '1')  //是雷
				{
					if (show[x][y] != '#')
						tag++;
					show[x][y] = '#';
					//mine[x][y] = '0';    //!!!!!!!!!!!!!!!!!!!!!
					print_board(show, row, col);
				}
				else
				{
					printf("排查错误,%d,%d处不为雷\n", x, y);
					printf("扫雷失败\n");
					print_board(mine, row, col);
					return;
				}
				break;
			}
			for (int i = 1; i <= row; i++)
			{
				for (int j = 1; j <= col; j++)
				{
					if (mine[i][j] == '0' && show[i][j] != '*')
						count++;
				}
			}
		}
		else
		{
			printf("输入错误\n");
		}
	}
	printf("扫雷成功!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
}

扫雷的主体函数

总结 

1. 有关为什么要用两个数组:一个用于存储雷的信息,一个用于存储扫出的信息。原因就略了,大致是因为这样方便吧。。。

2. 加了插旗子的操作,(和实际扫雷游戏还是有出入),当你判断某个地方确定是雷,可以进行插旗子操作,判断错误则游戏结束。当棋子个数等于雷数时,即全部找出。则胜利

3. 除了上一点中的胜利条件以外,还有一种方法就是,将所有的非雷区域找出即可。

4. 有关递归展开:当坐标xy周围雷数为0才展开,若非0则不展开。与实际游戏相符。 展开的条件:①. 该坐标周围雷数为0   ②.该坐标之前没有被展开或判断过(若展开过则会形成死递归,若被判断过,无非就是0或非0,也就相当于展开过了)。

5. 有关统计已经被排出的非雷点的方法:最开始用的是变量,每统计出一个就++。但是一直成功不了。后面改为了新办法,比较简单易懂:每次操作后统计一下即可。

待改进

当你把某个地方设为棋子之后,则这个地方相当于排除了,这个雷周围的点如果是1的话,那么再check时应该属于可以展开一片,但是目前这个功能还没实现。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值