"Data Structure and Algorithm" "Design and Implementation of Classical Algorithms" Chengdu University of Information Engineering dfs depth-first maze solution static maze dynamic generation maze graphical interface visualization c++

Author of this article: Tu Huan
Development environment and language: Dev-C++, EasyX, C++
Keywords: stack, depth-first, maze, visualization, static maze, dynamic maze, complexity, recursion


foreword

After studying "Data Structure and Algorithms", the school arranged a professional elective course "Design and Implementation of Classical Algorithms". My topic was "Maze Solving", because my major is digital media technology, and I prefer games. , and I also hope that it will be more interesting when working on the project, so I chose the maze game. The following are some excerpts from my final project report. I also uploaded a compressed package for the complete report, which contains all the files from the opening, design, and conclusion of the topic. If you need it, you can download it yourself. If you choose this topic from our school, congratulations on discovering the treasure!


1. Problem description

Mainly use the stack to achieve, requirestatic mazecan accurately find the path, and candynamically generated maze, to display all paths. useGUI displaythe path found.

2. Development environment and tools

3. Project design

1. Thinking Analysis

Here are my three main reference articles or videos at that time:

2. Data type

Stack (stack), also known as stack, is a linear table with limited operations. A linear list that restricts insertions and deletions to the end of the list only. This end is called the top of the stack, and the other end is called the bottom of the stack. Inserting a new element into a stack is also called pushing, pushing, or pushing. It is to put the new element on top of the top element of the stack to make it a new top element of the stack; deleting an element from a stack is also called stacking or stacking. Unstack, it is to delete the top element of the stack, so that the adjacent element becomes the new top element of the stack.

3. Core algorithm

  • Depth First Search (DFS)
  • Backtracking

4. Source code

//----------------------------------------库文件 --------------------------
#include <iostream>
#include<stdio.h>
#include <graphics.h>      // 引用图形库头文件
#include <conio.h>  //使用_getch();函数头文件 
#include <stack>  //stack是STL(标准模板库)提供的容器之一 
#include <Windows.h>
#include<time.h>//使用系统时间来初始化
#include<math.h>
#include<stdlib.h>//使用srand函数
using namespace std;

//-------------------------------------- 宏定义---------------------------
// 地图值: 1是墙壁,0是通道,2~5是主角的方向(上下左右),6是出口
#define WALL 1
#define PASS 0
#define UP 2
#define DOWN 3
#define LEFT 4
#define RIGHT 5
#define EXITS 6

//---------------------------------全局变量-----------------------------
int m = 10, n = 10;		// 行和列 默认为10
int map[100][100];		// 地图数组,先开大一点,根据m、n限定范围
int vis[100][100];		// 是否访问过,0是没访问,-1是访问过了
bool isFind = false;		// 是否找到出口
stack<int(*)> pathStack;// 用STL模板的栈类记录路径

//-----------------------------------加载图片-------------------------
IMAGE imgs[10];
void LoadImags() {
	loadimage(&imgs[0], "lu.png", 80, 80);
	loadimage(&imgs[1], "qiang.png", 80, 80);
	loadimage(&imgs[2], "u.png", 80, 80);
	loadimage(&imgs[3], "d.png", 80, 80);
	loadimage(&imgs[4], "l.png", 80, 80);
	loadimage(&imgs[5], "r.png", 80, 80);
	loadimage(&imgs[6], "qiang.png", 80, 80);
}

//-----------------------------------绘制地图--------------------------
void DrawMap() {
	Sleep(300); // 休眠0.3秒,更新图(300ms) 
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++)
		{
			putimage(j * 80, i * 80, &imgs[map[i][j]]);
		}
	}
}

//------------------------辅助函数,当前方向的对立方向-----------------
int GetAnotherDir(int dir) {
	if (dir == 2) {
		return 3;
	}
	if (dir == 3) {
		return 2;
	}
	if (dir == 4) {
		return 5;
	}
	if (dir == 5) {
		return 4;
	}
	return dir;
}

/*
思路:采用深度优先搜索
递归中自带“栈特性”,在进入当前递归函数相当于栈的push,退出当前递归函数相当于栈的pop
那么根据这个特性,实现搜索地图中返回上一个结点的功能。
*/

//---------------------------------------------寻找通路的函数----------------------------------------------- 
void DFS(int cury, int curx, int dir, int lasty, int lastx, int ysval) {// cury curx 表示当前点,dir是当前位置的方向, lasty,lastx 表示上一个点,ysval表示上一个点的地图值
	if (isFind) {// 找到了出口,递归栈都返回
		return;
	}
	if (vis[cury][curx] == -1 || map[cury][curx] == 1 || cury >= m || cury < 0 || curx >= n || curx < 0) {// 递归返回条件:这个位置访问过,墙壁,越界,
		return;
	}
	if (map[cury][curx] == EXITS) {// 递归退出条件,找到了出口
		//cout << "找到了出口" << endl;
		isFind = true;
		return;
	}

	// 递归函数执行时,执行相当于栈push时的操作
	pathStack.push(new int[3]{ cury, curx, dir });// 记录当前路径和方向
	vis[cury][curx] = -1;				   // 表示访问过当前点
	int curysval = map[cury][curx];			// 保存当前的地图值
	// 更新主角所在位置。
	map[lasty][lastx] = ysval;				// 上一个所在的位置变回地图值
	map[cury][curx] = dir;					// 主角现在所在的位置 = 主角面向方向值
	DrawMap();								// 递归函数执行时,当前的地图值
	// 上下左右四个方向探索,并传入当前位置和当前的地图值(以便在下一个递归函数把当前主角所占的位置变回地图值)
	DFS(cury + 1, curx, DOWN, cury, curx, curysval);// 先向下边探索,这里先往下搜索,对比一下先向右边探索
	DFS(cury, curx + 1, RIGHT, cury, curx, curysval);// 向右边探索
	DFS(cury, curx - 1, LEFT, cury, curx, curysval);// 向左边探索
	DFS(cury - 1, curx, UP, cury, curx, curysval);// 向上边探索

	if (isFind) {// 找到了出口,现在的路径栈保存了正确的访问路径,退出即可,但是这个路径可能不是最优路径
		return;
	}
	// 4个方向都走完了,无路可走,需要返回上一个位置
	// 递归函数结束时,相当于栈pop时的操作
	// 需要把递归函数进入时改变的数值等操作回归原位
	//vis[cury][curx] = 0;						// 当前点没访问过,去掉没影响,但恢复为0可能可以找到最优路径
	pathStack.pop();							// 舍去记录当前位置
	map[cury][curx] = curysval;				// 现在所在的位置变回一开始的地图值
	map[lasty][lastx] = GetAnotherDir(dir);	// 主角返回到上一个位置,上一个位置的方向,是当前对立的方向才能绘图正确
	DrawMap();								// 绘制当前的地图值
}

//------------------------------------------------根据栈保存的路径值,修改地图值----------------------------------------------------
void UpdateMapToPath() {
	while (!pathStack.empty()) {
		int* pos = pathStack.top();
		pathStack.pop();
		map[*pos][*(pos + 1)] = *(pos + 2); // 变成主角当时的方向值
	}
}
//------------------------------------------------------迷宫初始化---------------------------------------------------------
 void InitMaze(int Rank2,int x, int y) 
{
	//墙和路径的标识
	int BAR = 0 ;
 	int ROUTE = 1 ;
 	
	map[x][y] = ROUTE;
 
	//确保四个方向随机
	int direction[4][2] = { { 1,0 },{ -1,0 },{ 0,1 },{ 0,-1 } };
	for (int i = 0; i < 4; i++) {
		int r = rand() % 4;
		int temp = direction[0][0];
		direction[0][0] = direction[r][0];
		direction[r][0] = temp;
 
		temp = direction[0][1];
		direction[0][1] = direction[r][1];
		direction[r][1] = temp;
	}
 
	//向四个方向开挖
	for (int i = 0; i < 4; i++) {
		int dx = x;
		int dy = y;
 
		//控制挖的距离,由Rank来调整大小
		int range = 1 + (Rank2 == 0 ? 0 : rand() % Rank2);
		while (range>0) {
			dx += direction[i][0];
			dy += direction[i][1];
 
			//排除掉回头路
			if (map[dx][dy] == ROUTE) {
				break;
			}
 
			//判断是否挖穿路径
			int count = 0;
			for (int j = dx - 1; j < dx + 2; j++) {
				for (int k = dy - 1; k < dy + 2; k++) {
					//abs(j - dx) + abs(k - dy) == 1 确保只判断九宫格的四个特定位置
					if (abs(j - dx) + abs(k - dy) == 1 && map[j][k] == ROUTE) {
						count++;
					}
				}
			}
 
			if (count > 1) {
				break;
			}
 
			//确保不会挖穿时,前进
			--range;
			map[dx][dy] = ROUTE;
		}
 
		//没有挖穿危险,以此为节点递归
		if (range <= 0) {
			InitMaze(Rank2,dx,dy);
		}
	}
}
//--------------------------------------------------创建迷宫----------------------------------------------------------
void CreateMaze(int Rank1,int L,int* pendx,int* pendy) 
{
	int BAR = 0 ;//墙和路径的标识
 	int ROUTE = 1 ;//墙和路径的标识
 	
	srand((unsigned)time(NULL));//随机数种子
 
	//最外围层设为路径的原因,为了防止挖路时挖出边界,同时为了保护迷宫主体外的一圈墙体被挖穿
	for (int i = 0; i < L; i++){
		map[i][0] = ROUTE;
		map[0][i] = ROUTE;
		map[i][L - 1] = ROUTE;
		map[L - 1][i] = ROUTE;
	}
 
	//创造迷宫,(2,2)为起点
	InitMaze(Rank1,2,2);
 
	//画迷宫的入口和出口
	map[2][1] = ROUTE;//迷宫的入口  也就是  游戏人物的起点 
 
	//由于算法随机性,出口有一定概率不在(L-3,L-2)处,此时需要寻找出口
	for (int i = L - 3; i >= 0; i--) {
		if (map[i][L - 3] == ROUTE) {
			map[i][L - 2] = ROUTE;
			*pendy = i;
			*pendx = L-1;
			break;
		}
	}
 
 
	//1、0替换 
	for (int i = 1; i < L-1; i++) {
		for (int j = 1; j < L-1; j++) {
			if (map[i][j] == ROUTE) {
				map[i][j] = PASS;
			}
			else {
				map[i][j] = WALL;
			}
		}
	}
	
}

//--------------------------------------------------------主函数-------------------------------------------------------------- 
int main()
{
	//变量声明 
	int val;
	int endx,endy,curx,cury;
//-------------------------------------------------------模式选择------------------- 
	//模式选择  :    0  随机生成迷宫        1  手动输入迷宫
	int pattern; 
	cout << "请选择构建迷宫的方式:\n0.随机生成迷宫\n1.手动输入迷宫\n" << endl; //实际上不是0就可以手动输入 
	cin >> pattern ;
//-----------------------------------------------0   随机生成迷宫-------------------------------------	
	if(pattern == 0) {  
	int L;
	cout << "请输入构建迷宫的大小:" << endl;
	cin >> L;
	m = L;
	n = L;
	int Rank;
	cout << "请输入构建迷宫的复杂度Rank:(Rank越大迷宫越简单,Rank最小值为0)" << endl;
	cin >> Rank;

	CreateMaze(Rank,L,&endx,&endy); //对迷宫数据进行初始化   map[i][j]
	
	//!!!!!!!设置迷宫的出口和入口
	//endy = *pendy, endx = *pendx; // !!!出口列和行,列是x,y是行
	curx = 1 , cury = 2;			// !!!主角开始行和列
	map[endy][endx] = EXITS;
		
}
// -------------------------------------------1   手动输入迷宫--------------------------------------------------
	else{ 
	cout << "请输入地图行数和列数:" << endl;
	cin >> m >> n;
	cout << "请输入地图数据:" << endl;
	//根据输入初始化地图
	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++){
			cin >> val;
			map[i][j] = val;
		}
	}
	//!!!!!!!设置迷宫的出口和入口
	endy = m - 2, endx = n - 1; // !!!出口列和行,列是x,y是行
	curx = 1, cury = 1;			// !!!主角开始行和列
	map[endy][endx] = EXITS;// 给出口位置标记
	
}

	
	initgraph(n * 80, m * 80,SHOWCONSOLE);		// 创建绘图窗口,大小为 n * 80,m * 80 像素
	LoadImags();
	
	// 2.深度优先搜索,找到路径
	DFS(cury, curx, DOWN, cury, curx, map[cury][curx]);

	// 3.根据栈保存的信息,修改地图访问的路径
	UpdateMapToPath();
	DrawMap();

	_getch();              // 按任意键继续
	closegraph();          // 关闭绘图窗口

}


//----------------------------------参考数据-------------------------------------- 
/*
地图值1: 
10 10 
1 1 1 1 1 1 1 1 1 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 0 0 1 1 0 0 1 
1 0 1 1 1 0 0 0 0 1 
1 0 0 0 1 0 0 0 0 1 
1 0 1 0 0 0 1 0 0 1 
1 0 1 1 1 0 1 1 0 1 
1 0 0 0 0 1 0 0 0 1 
1 1 1 1 1 1 1 1 1 1 
地图值2: 
11 10 
1 1 1 1 1 1 1 1 1 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 0 0 1 1 0 0 1 
1 0 1 1 1 0 0 0 0 1 
1 0 0 0 1 0 1 0 0 1 
1 0 1 0 0 0 1 0 0 1 
1 0 1 1 1 0 1 1 0 1 
1 0 0 0 0 1 0 1 0 1 
1 0 1 0 0 0 0 1 0 1 
1 1 1 1 1 1 1 1 1 1
*/

代码直接复制粘贴运行是看不到图形界面的,需要在代码文件的同级文件夹里保存6张供加载的照片,需要的可以私信我,当然,也可以使用自己喜欢的图片

5. Operation effect

1. Static maze

(1) Test data:

  • 1

  • 10 10

  • 1 1 1 1 1 1 1 1 1 1
    1 0 0 1 0 0 0 1 0 1
    1 0 0 1 0 0 0 1 0 1
    1 0 0 0 0 1 1 0 0 1
    1 0 1 1 1 0 0 0 0 1
    1 0 0 0 1 0 0 0 0 1
    1 0 1 0 0 0 1 0 0 1
    1 0 1 1 1 0 1 1 0 1
    1 0 0 0 0 1 0 0 0 1
    1 1 1 1 1 1 1 1 1 1

(2) Effect:

static maze

2. Dynamically generate a maze

(1) Test data:

  • 0
  • 10
  • 0

(2) Effect:

dynamically generated maze


Like it if it helps you! If you don't understand something, you can leave a message or send a private message. If you see it, you will reply in time!

Guess you like

Origin blog.csdn.net/weixin_64072619/article/details/128536292