数据结构课设:迷宫问题(队列求解)

一、题目要求

        给定一个n*m的迷宫,0表示迷宫中的通路,1表示迷宫中的墙壁,给定起点和终点,求解从起点到终点的所有通路,或者没有找到通路的结论。

二、需求分析

1、使用文件读入迷宫地图和迷宫的起点与终点。

2、可视化显示迷宫。

3、使用链表作为数据结构的队列类型求解迷宫问题。

4、判断迷宫是否有通路,有则以三元组( i,j,d)的形式输出。其中 i, j ,指示迷宫中的一个坐标,d表示走到下一坐标的方向,没有则输出迷宫没有通路。

5、如果有通路则以方阵形式输出迷宫和通路。

6、显示迷宫最短路径。

三、设计过程

3.1概要设计

      首先使用文件读入方式读入迷宫数据,把迷宫数据传给绘图函数进行可视化显示迷宫地图,使用队列求出通路并记录下来,最后把数据传给绘图函数进行可视化展示。

3.2详细设计

3.2.1读入迷宫

      使用fopen函数打开迷宫文件,然后判断是否打开成功,成功则使用fscanf函数读取文件的迷宫数据和起点与终点。

3.2.2显示迷宫

      定义枚举类型,0代表空格(Space),1代表墙壁(Wall),2代表走过的路径(Gamer),3代表起点(start),4代表终点(endd),对地图进行遍历,使用easyx的fillrectangle函数对墙壁进行可视化绘图。

3.2.3求解所有可能通路

      为了保存路径,定义一个结构体记录信息,用col,colume记录当前所在的位置,用pre记录当前位置由哪一个位置得来,用dir记录由哪一个方向得来,用dirr记录当前位置往哪一个方向走,以便得到路径三元组(i,j,d)。

       Step1:定义一个队列,将起始点放入队列中。

      Step2:取出队头元素,并将其从队列中删除,判断其是否为终点,如果是,则保存路径,根据pre逆向获取路径,并保存至stk数组中,以便使用。

      Step3:对队头进行东、南、西、北四个方向进行扩展,再对待扩展的结点进行判断,如果待扩展的结点不为墙,并且在当前路径中没有出现过,即为满足条件的点,可加入队列,并记录结点属性,如此即可通过队列求得所有满足条件的路径。

        3.2.4输出迷宫及其通路

       把迷宫地图中需要走的路径标记为枚举类型中的Gamer,然后再使用fillrectangle函数进行迷宫及其通路的绘图即可。

        3.2.5显示最短路径

       求得的最短路径即为stk数组中的第一条路径,使用draw()函数可视化输出即可。

四、实现效果

迷宫路径动态展示

 五、编程代码

#include<stdio.h>
#include<graphics.h>
#include<assert.h>
#include<string.h>
#include<stdbool.h>
#include <conio.h>
#include<iostream>
#include<string>
#define GRID_SIZE 50
#define MaxSize 100000
#define M 100
#define N 100
int cnt = 0;		//记录路径的数量
using namespace std;
int map[M][N];
int mapp[M][N];
int n, m;
int sx, sy, ex, ey;
enum SpriteType {
	Space,
	Wall,
	Gamer,
	start,
	endd
};
IMAGE img_gamer;
struct Button
{
	int x;
	int y;
	int w;
	int h;
	COLORREF curColor;
	COLORREF inColor;
	COLORREF outColor;
	char* text;
};
struct Button* createButton(int x, int y, int w, int h, const char* str, COLORREF inColor, COLORREF outColor) {
	struct Button* pB = (struct Button*)malloc(sizeof(struct Button));
	assert(pB);
	pB->x = x;
	pB->y = y;
	pB->w = w;
	pB->h = h;
	pB->inColor = inColor;
	pB->outColor = outColor;
	pB->curColor = pB->outColor;
	int textLength = strlen(str) + 1;
	pB->text = (char*)malloc(sizeof(char) * textLength);
	assert(pB->text);
	strcpy_s(pB->text, textLength, str);
	return pB;
}
//画按钮
void drawbutton(struct Button* pB) {
	setfillcolor(pB->curColor);
	fillrectangle(pB->x, pB->y, pB->x + pB->w, pB->y + pB->h);
	settextcolor(BLACK);
	setbkmode(TRANSPARENT);
	settextstyle(15, 0, "楷体");
	int textw = textwidth(pB->text);
	int texth = textheight(pB->text);
	int xx = pB->x + (pB->w - textw) / 2;
	int yy = pB->y + (pB->h - texth) / 2;
	outtextxy(xx, yy, pB->text);
}
//判断是否为按钮
bool isInButton(struct Button* pB, ExMessage m) {
	if (m.x > pB->x && m.x<pB->x + pB->w && m.y>pB->y && m.y < pB->y + pB->h) {
		pB->curColor = pB->inColor;
		return true;
	}
	pB->curColor = pB->outColor;
	return false;
}
//判断是否点击按钮
bool isClickButton(struct Button* pB, ExMessage m) {
	if (isInButton(pB, m) && m.message == WM_LBUTTONDOWN) {
		return true;
	}
	return false;
}
void readmap(const char* filename, int map[M][N]) {
	FILE* fp = fopen(filename, "r");
	if (!fp) {
		perror("file open failed~");
		return;
	}
	//读取数据
	fscanf(fp, "%d ", &n);		//n表示迷宫的行数
	fscanf(fp, "%d ", &m);		//m表示迷宫的列数
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			fscanf(fp, "%d ", &map[i][j]);
		}
	}
	fscanf(fp, "%d ", &sx);		//sx表示起点x坐标
	fscanf(fp, "%d ", &sy);		//sy表示起点y坐标
	fscanf(fp, "%d ", &ex);		//ex表示终点x坐标
	fscanf(fp, "%d ", &ey);		//ey表示终点y坐标
	fclose(fp);
}
void init()
{
	loadimage(&img_gamer, "C:/Users/XHX/Pictures/Camera Roll/th.jpg", GRID_SIZE, GRID_SIZE);//加载图片
}
void draw(int mapp[M][N]){
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			int x = i * GRID_SIZE, y = j * GRID_SIZE+110;
			switch (mapp[i][j]) {
			case Space:
				break;
			case Wall:
				setfillcolor(RGB(93, 107, 153));
				fillrectangle(y, x, y + GRID_SIZE, x + GRID_SIZE);
				break;
			case Gamer:
				putimage(y, x, &img_gamer);		//绘制图片
				break;
			case start:
				setfillcolor(RGB(0, 0, 170));
				fillrectangle(y, x, y + GRID_SIZE, x + GRID_SIZE);
				break;
			case endd:
				setfillcolor(RED);
				fillrectangle(y, x, y + GRID_SIZE, x + GRID_SIZE);
				break;
			default:
				break;
			}
		}
	}
}
typedef struct
{
	int col, colume;						//方块的位置
	int pre;						//本路径中上一方块在队列中的下标
	int dir, dirr;					//dir记录从哪一个方向转移来,dirr记录往哪一个方向转移
} Box;								//方块类型
typedef struct
{
	Box data[MaxSize];
	int front, rear;					//队头指针和队尾指针
} QuType;	//顺序队类型
struct boxx {
	Box data[MaxSize];
	int sz;
}stk[100];
void InitQueue(QuType*& q)			//初始化队列
{
	q = (QuType*)malloc(sizeof(QuType));
	q->front = q->rear = -1;
}
void DestroyQueue(QuType*& q)		//销毁队列
{
	free(q);
}
bool QueueEmpty(QuType* q)			//判断队列是否为空
{
	return(q->front == q->rear);
}
bool enQueue(QuType*& q, Box e)		//进队列
{
	if (q->rear == MaxSize - 1)			//队满上溢出
		return false;				//返回假
	q->rear++;						//队尾增1
	q->data[q->rear] = e;				//rear位置插入元素e
	return true;					//返回真
}
bool deQueue(QuType*& q, Box& e)	//出队列
{
	if (q->front == q->rear)			//队空下溢出
		return false;
	q->front++;
	e = q->data[q->front];
	return true;
}
void request(QuType* qu, int front)	//从队列qu中输出路径
{
	cnt++;
	int k = front, j, ns = 0;
	do				
	{
		j = k;
		k = qu->data[k].pre;
		qu->data[k].dirr = qu->data[j].dir;
		stk[cnt].data[++ns] = qu->data[j];
	} while (k != 0);
	stk[cnt].data[++ns] = qu->data[0];
	stk[cnt].sz = ns;
}
void print()
{
	settextstyle(15, 0, "楷体");
	string s;
	char demo[1000000];
	if (cnt == 0) {
		settextstyle(50, 0, "楷体");
		s = "迷宫没有通路";
		strcpy_s(demo, s.c_str());
		outtextxy(110, 0, demo);
	}
	else {
		int num = 0;
		for (int i = 1; i <= cnt; i++) {
			string s = "第" + to_string(i) + "条路径为:";
			strcpy_s(demo, s.c_str());
			outtextxy(110,  20 * num, demo);
			num++;
			s = "";
			for (int j = stk[i].sz; j; j--) {
				if (j != 1)
					s = s + '(' + to_string(stk[i].data[j].col) + ',' + to_string(stk[i].data[j].colume) + ',' + to_string(stk[i].data[j].dirr) + ')' + ' ';
				else s = s + '(' + to_string(stk[i].data[j].col) + ',' + to_string(stk[i].data[j].colume) + ')';
				if ((stk[i].sz - j+1) % 5 == 0) {
					strcpy_s(demo, s.c_str());
					outtextxy(110, 20 * num , demo);
					s = "";
					num++;
				}
			}
			strcpy_s(demo, s.c_str());
			outtextxy(110, 20 * num, demo);
			num++;
			s = "";
		}
	}
}
void mappath1(int startx, int starty, int endx, int endy)	
{
	Box stepHead, stepNext;
	int direct, dir1;
	bool CheckStepNext;
	QuType* qu;						//定义顺序队指针qu
	InitQueue(qu);					//初始化队列qu
	stepHead.col = startx; stepHead.colume = starty; stepHead.pre = -1;
	enQueue(qu, stepHead);			//将起点加入队列中
	while (!QueueEmpty(qu))			//队不空且循环
	{
		deQueue(qu, stepHead);			//出队方块e,由于不是环形队列,该出队元素仍在队列中
		stepHead = stepHead;
		if (stepHead.col == endx && stepHead.colume == endy)	//找到了出口,输出路径
		{
			request(qu, qu->front);
			continue;	//调用print函数输出路径
		}
		for (direct = 0; direct < 4; direct++)		//循环扫描每个方位,把每个可走的方块插入队列中
		{
			CheckStepNext = true;			//判断当前待扩展结点可不可行,开始默认可行
			switch (direct)
			{
			case 0:stepNext.col = stepHead.col - 1; stepNext.colume = stepHead.colume;   break;
			case 1:stepNext.col = stepHead.col;   stepNext.colume = stepHead.colume + 1; break;
			case 2:stepNext.col = stepHead.col + 1; stepNext.colume = stepHead.colume;   break;
			case 3:stepNext.col = stepHead.col;   stepNext.colume = stepHead.colume - 1; break;
			}
			if (map[stepNext.col][stepNext.colume] == 0)
			{
				for (int i = qu->front; i >= 0; i = qu->data[i].pre) {
					if (qu->data[i].col == stepNext.col && qu->data[i].colume == stepNext.colume) {
						CheckStepNext = false;		//与当前路径冲突,即当前待扩展结点不可行
						break;
					}
				}
				if (CheckStepNext) {
					stepNext.dir = direct;
					stepNext.pre = qu->front;
					enQueue(qu, stepNext);
				}
			}
		}
	}
}
int main() {
	readmap("C:/Users/XHX/Desktop/迷宫.txt", map);
	mappath1(sx, sy, ex, ey);
	initgraph(16 * GRID_SIZE, 12*GRID_SIZE);
	setbkcolor(WHITE);
	cleardevice();
	init();
	struct Button* button1 = createButton(0,0, 100, 50, "输出迷宫", RGB(236, 244, 255),RGB(204,213,240));
	struct Button* button2 = createButton(0, 60, 100, 50, "显示迷宫通路", RGB(236, 244, 255), RGB(204, 213, 240));
	struct Button* button3 = createButton(0, 120, 100, 50, "显示路径", RGB(236, 244, 255), RGB(204, 213, 240));
	struct Button* button4 = createButton(0, 180, 100, 50, "动态显示通路", RGB(236, 244, 255), RGB(204, 213, 240));
	struct Button* button5= createButton(0, 240, 100, 50, "动态最短通路", RGB(236, 244, 255), RGB(204, 213, 240));
	struct Button* button6 = createButton(0, 300, 100, 50, "静态最短通路", RGB(236, 244, 255), RGB(204, 213, 240));
	ExMessage mes;
	BeginBatchDraw();
	while (1) {
		drawbutton(button1);
		drawbutton(button2);
		drawbutton(button3);
		drawbutton(button4);
		drawbutton(button5);
		drawbutton(button6);
		peekmessage(&mes, EM_MOUSE);
		if (isClickButton(button1, mes))
		{
			cleardevice();
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < m; j++) {
					mapp[i][j] = map[i][j];
				}
			}
			mapp[sx][sy] = 3;
			mapp[ex][ey] = 4;
			draw(mapp);
		}
		if (isClickButton(button2, mes)) {
			cleardevice();
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < m; j++) {	//将原始map复制给mapp,避免对原始地图的破坏
					mapp[i][j] = map[i][j];
				}
			}
			for (int i = 1; i <= cnt; i++) {
				for (int j = stk[i].sz; j; j--) {	//修改地图上的点为路径上的点的枚举类型
					mapp[stk[i].data[j].col][stk[i].data[j].colume] = 2;
				}
			}
			mapp[sx][sy] = 3;		//修改起点
			mapp[ex][ey] = 4;		//修改终点
			draw(mapp);
		}
		if (isClickButton(button3, mes)) {
			cleardevice();
			print();
		}
		if (isClickButton(button4, mes)) {
			int r = sx, c = sy;
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < m; j++) {
					mapp[i][j] = map[i][j];		//将原始map复制给mapp,避免对原始地图的破坏
				}
			}
			mapp[sx][sy] = 3;
			mapp[ex][ey] = 4;
			for (int i = 1; i <= cnt; i++) {
				for (int j = stk[i].sz-1; j; j--) {
					BeginBatchDraw();	//开启双缓冲绘图
					cleardevice();
					mapp[stk[i].data[j].col][stk[i].data[j].colume] = 2;//每次只修改一个点
					if (j != stk[i].sz && j + 1 != stk[i].sz) mapp[stk[i].data[j + 1].col][stk[i].data[j + 1].colume] = 0;
					draw(mapp);
					mapp[ex][ey] = 4;
					Sleep(200);
					FlushBatchDraw();	//结束双缓冲绘图
				}
			}
		}
		if (isClickButton(button5, mes)) {
			cleardevice();
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < m; j++) {	//将原始map复制给mapp,避免对原始地图的破坏
					mapp[i][j] = map[i][j];
				}
			}
			mapp[sx][sy] = 3;
			mapp[ex][ey] = 4;
			for (int i = 1; i <= cnt; i++) {
				if (i != 1 && stk[i].sz != stk[i - 1].sz) break;
				for (int j = stk[i].sz-1; j; j--) {
					BeginBatchDraw();	//开启双缓冲绘图
					cleardevice();
					mapp[stk[i].data[j].col][stk[i].data[j].colume] = 2;//每次只修改一个点
/*复原修改的点*/	if (j != stk[i].sz && j + 1 != stk[i].sz) mapp[stk[i].data[j + 1].col][stk[i].data[j + 1].colume] = 0;
					draw(mapp);
					mapp[ex][ey] = 4;
					Sleep(200);
					FlushBatchDraw();	//结束双缓冲绘图
				}
			}
		}
		if (isClickButton(button6, mes)) {
			cleardevice();
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < m; j++) {	//将原始map复制给mapp,避免对原始地图的破坏
					mapp[i][j] = map[i][j];
				}
			}
			for (int j = stk[1].sz; j; j--) {	//修改地图上的点为路径上的点的枚举类型
				mapp[stk[1].data[j].col][stk[1].data[j].colume] = 2;
			}
			mapp[sx][sy] = 3;		//修改起点
			mapp[ex][ey] = 4;		//修改终点
			draw(mapp);
		}
		FlushBatchDraw();
	}
	EndBatchDraw();
	closegraph();
	return 0;
}

  • 16
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值