前言
最近刚好在写自己的课设,匆匆忙忙写出来的课设系统,仍有不足,拿出来和大家分享一下,希望能对大家有帮助。
一、概要设计
1、基本信息
具体功能包括:
(1) 选择创建的迷宫的方式:文件读取、用户输入、系统随机生成。
(2) 向用户展示生成的迷宫
(3) 选择求解的方式(DFS or BFS)
(4) 向用户展示迷宫路径
(5) 用户选择是否去掉多余路径
开发环境:VS2019
2、功能模块图
3、功能描述
(1) 迷宫的创建:文件读取(提前在文件中写好迷宫直接读取),用户输入(用户手动输入),系统随机生成(rand%2的方式随机生成迷宫)
(2) 求解方式:DFS(通过栈,以及DFS回溯的方式求解),BFS(通过队列,以及BFS穷举的方式求解)
(3) 去掉多余路径:多余标记全部删除
4、调用关系图
5、结果演示
提前在源文件同一目录下创建好这两个文件
① 创建迷宫
读取文件:
提前在文件 maze_information.txt 中写好,迷宫矩阵:
用户输入:
系统随机生成:
② 求解
栈+DFS:
队列 + BFS:
③ 清除多余路径
栈+DFS:
队列 + BFS:
二、完整代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<Windows.h>
#include<time.h>
typedef int Status; // 下一个通道方向
#define RANGE 100 // 迷宫大小
#define STACK_INIT_SIZE 100 // 栈的初始大小
#define STACKINCREMENT 10 // 栈的储存增量
#define ROW 10 // 迷宫的行数
#define COL 10 // 迷宫的列数
// 迷宫
typedef struct
{
int m, n;
int arr[RANGE][RANGE]; // 迷宫数组
}MazeType;
//坐标(row,col)
typedef struct
{
int row; // 迷宫中的行
int col; // 迷宫中的列
}PosType;
// 路径
typedef struct
{
int step; // 当前位置在路径上的"序号"
PosType seat; // 当前坐标
Status di; // 往下一个坐标位置的方向
}SElemType;
// 定义栈
typedef struct
{
SElemType* base; // 栈底
SElemType* top; // 栈顶
int stacksize; // 栈长
}SqStack;
// 定义队列
typedef struct
{
PosType seat; // 当前坐标
int pre; // 父节点
}Strnode;
Strnode que[ROW * COL];
// 原始迷宫,其中'1'表示墙,'0'表示通道
int a[ROW][COL];
// 创建迷宫
void ChoiceMazeInterface(); // 选择创建方式界面
int ReadMaze(int a[ROW][COL]); // 读取文件中迷宫
int WriteMaze(int a[ROW][COL]); // 用户输入迷宫
int CreateMaze(int a[ROW][COL]); // 随机迷宫
int SaveMaze(MazeType& maze); // 保存文件
// 栈的实现
int InitStack(SqStack& s); // 栈的初始化
int GetTop(SqStack s, SElemType& e); // 当栈s不为空时,返回栈顶e
int Push(SqStack& s, SElemType e); // 入栈
int Pop(SqStack& s, SElemType& e); // 出栈
int StackEmpty(SqStack s); // 判空栈
int DestoryStack(SqStack& s); // 销毁栈
// 迷宫操作
int InitMaze(MazeType& maze, int a[ROW][COL]); // 初始化迷宫
int Pass(MazeType maze, PosType curpos); // 判断当前节点是否走过
int FootPrint(MazeType& maze, PosType curpos); // 标记可以通过
int MarkPrint(MazeType& maze, PosType curpos); // 标记不能通过
SElemType CreateSElem(int step, PosType pos, int di); // 创建路径
PosType NextPos(PosType curpos, Status di); // 移动
int PosEqual(PosType pos1, PosType pos2); // 判断是不是出口
void PrintMaze(MazeType maze, int row, int col); // 打印路径
// 实现方法
void Welcome(); // 迷宫路径界面
int DepthFirstInterface(MazeType& maze, PosType start, PosType end); // 深搜界面
int DepthFirstMazePath(MazeType& maze, PosType start, PosType end); // 深搜求解迷宫maze中,从入口start到出口end的一条路径
int BreadthFirstInterface(MazeType& maze, PosType start, PosType end); // 广搜界面
int BreadthFirstMazePath(MazeType& maze, PosType start, PosType end); // 广搜求解迷宫maze中,从入口start到出口end的一条路径
void Sign(MazeType& maze, int ans); // 广搜标记地图
void Clean(MazeType& maze); // 清除错误路径
void GoodBye(); // 结束
int main()
{
int i, j;
int choice;
PosType start, end; //开始,终点坐标
MazeType maze;
while (1)
{
ChoiceMazeInterface();
scanf("%d", &choice);
switch (choice)
{
case 1:
ReadMaze(a);
break;
case 2:
WriteMaze(a);
break;
case 3:
CreateMaze(a);
break;
default:
GoodBye();
break;
}
while (1)
{
InitMaze(maze, a); // 初始化迷宫
system("cls");
printf("\n-------------------------------------------------\n");
printf("\n迷宫如下:\n");
PrintMaze(maze, ROW, COL);
printf("\n-------------------------------------------------\n");
printf("\n请输入迷宫起点坐标:");
scanf("%d %d", &start.row, &start.col);
printf("\n请输入迷宫终点坐标:");
scanf("%d %d", &end.row, &end.col);
Welcome();
scanf("%d", &choice);
switch (choice)
{
case 1:
if (DepthFirstInterface(maze, start, end))
{
system("cls");
printf("\n-------------- 栈 + 深搜 --------------\n");
Clean(maze);
}
break;
case 2:
if (BreadthFirstInterface(maze, start, end))
{
system("cls");
printf("\n-------------- 队列 + 广搜 --------------\n");
Clean(maze);
}
break;
case 0:
GoodBye();
break;
default:
break;
}
SaveMaze(maze);
printf("\n-------------------------------------------------\n");
printf("是否需要继续对该迷宫操作?(Yes:1 / No:0):");
scanf("%d", &choice);
if (choice == 0)
break;
}
}
GoodBye();
}
// 选择创建方式界面
void ChoiceMazeInterface()
{
system("cls");
printf("****************************************************************\n");
printf("*********** 迷宫创建 ***********\n");
printf("*********** 1 ---- 读取文件 ***********\n");
printf("*********** 2 ---- 用户输入 ***********\n");
printf("*********** 3 ---- 随机生成 ***********\n");
printf("*********** else ---- 退出 ***********\n");
printf("****************************************************************\n");
printf("请选择想要创建迷宫的方式(数字):");
}
// 读取文件
int ReadMaze(int a[ROW][COL])
{
FILE* pfRead = fopen("maze_information.txt", "r");
if (pfRead == NULL)
{
return 0;
}
for (int i = 1; i < ROW - 1; i++)
{
for (int j = 1; j < COL - 1; j++)
{
fscanf(pfRead, "%d", &a[i][j]);
}
fscanf(pfRead, "\n");
}
fclose(pfRead);
return 1;
}
// 保存文件
int SaveMaze(MazeType& maze)
{
FILE* pfRead = fopen("maze_answer.txt", "w");
if (pfRead == NULL)
{
return 0;
}
for (int i = 1; i < ROW - 1; i++)
{
for (int j = 1; j < COL - 1; j++)
{
fprintf(pfRead, "%d", maze.arr[i][j]);
}
fprintf(pfRead, "\n");
}
fclose(pfRead);
return 1;
}
// 用户输入迷宫
int WriteMaze(int a[ROW][COL])
{
printf("\n-------------------------------------------------\n");
printf("\n请输入原始迷宫:(8×8)\n");
printf("(其中'1'表示墙,'0'表示通道)\n");
for (int i = 1; i < ROW - 1; i++)
{
printf("请输入第 %d 行:", i);
for (int j = 1; j < COL - 1; j++)
{
scanf("%d", &a[i][j]);
}
}
return 1;
}
// 随机迷宫
int CreateMaze(int a[ROW][COL])
{
srand(time(0));
for (int i = 1; i < ROW - 1; i++)
{
for (int j = 1; j < COL - 1; j++)
{
if ((rand() % 100 + 1) % 2 == 0)
a[i][j] = 0;
else
a[i][j] = 1;
}
}
return 1;
}
// 栈的初始化
int InitStack(SqStack& s)
{
s.base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!s.base)
exit(0);
s.top = s.base;
s.stacksize = STACK_INIT_SIZE;
return 1;
}
// 当栈s不为空时,返回栈顶e
int GetTop(SqStack s, SElemType& e)
{
// 判空栈
if (s.top == s.base)
return 0;
e = *(s.top - 1);
return 1;
}
// 入栈
int Push(SqStack& s, SElemType e)
{
// 判满栈
if (s.top - s.base >= s.stacksize)
{
// 若栈满,追加存储空间
s.base = (SElemType*)realloc(s.base, (s.stacksize + STACKINCREMENT) * sizeof(SElemType));
if (!s.base)
exit(0);
s.top = s.base + s.stacksize;
s.stacksize += STACKINCREMENT;
}
*s.top++ = e;
return 1;
}
// 出栈
int Pop(SqStack& s, SElemType& e)
{
// 判空栈
if (s.top == s.base)
return 0;
e = *--s.top; //e指向新栈顶
return 1;
}
// 判空栈
int StackEmpty(SqStack s)
{
return s.base == s.top;
}
// 销毁栈
int DestoryStack(SqStack& s)
{
free(&s);
return 1;
}
// 初始化迷宫
int InitMaze(MazeType& maze, int a[ROW][COL])
{
// 设置迷宫maze的初值,包括加上边缘一圈的值
int i, j;
for (i = 1; i < ROW - 1;i++)
{
for (j = 1; j < COL - 1; j++)
{
maze.arr[i][j] = a[i][j];
}
}
//加上围墙
for (j = 0; j < ROW; j++)
maze.arr[0][j] = maze.arr[ROW - 1][j] = 1;
for (i = 0; i < COL; i++)
maze.arr[i][0] = maze.arr[i][COL - 1] = 1;
return 1;
}
// 判断当前节点是否走过
int Pass(MazeType maze, PosType curpos)
{
if (maze.arr[curpos.row][curpos.col] == 0)
return 1;
else
return 0;
}
// 标记可以通过
int FootPrint(MazeType& maze, PosType curpos)
{
// 标记为2表示可以通过
maze.arr[curpos.row][curpos.col] = 2;
return 1;
}
// 标记不能通过
int MarkPrint(MazeType& maze, PosType curpos)
{
// 标记为3表示不能通过
maze.arr[curpos.row][curpos.col] = 3;
return 1;
}
// 创建路径
SElemType CreateSElem(int step, PosType pos, int di)
{
SElemType e;
e.step = step;
e.seat = pos;
e.di = di;
return e;
}
// 移动
PosType NextPos(PosType curpos, Status di)
{
// 返回当前节点的下一节点
PosType pos = curpos;
switch (di)
{
case 1: //右
pos.col++;
break;
case 2: //下
pos.row++;
break;
case 3: //左
pos.col--;
break;
case 4: //上
pos.row--;
break;
}
return pos;
}
// 判断是不是出口
int PosEqual(PosType pos1, PosType pos2)
{
if (pos1.row == pos2.row && pos1.col == pos2.col)
return 1;
else
return 0;
}
// 打印路径
void PrintMaze(MazeType maze, int row, int col)
{
int i, j;
printf(" ");
for (i = 0; i < col; i++)
printf("%d ", i);
printf("\n");
for (i = 0; i < row; i++)
{
printf("%d ",i);
for (j = 0; j < col; j++)
{
switch (maze.arr[i][j])
{
case 0:
printf(" "); // 没走过,但是通路
break;
case 1:
printf("■"); // 墙,障碍物
break;
case 2:
printf("* "); // 走过且走得通
break;
case 3:
printf("@ "); // 走过但走不通,死胡同
break;
default:
break;
}
}
printf("\n");
}
}
// 迷宫路径界面
void Welcome()
{
system("cls");
printf("****************************************************************\n");
printf("*********** 迷宫路径 ***********\n");
printf("*********** 1 ---- 栈 + 深搜 ***********\n");
printf("*********** 2 ---- 队列 + 广搜 ***********\n");
printf("*********** 0 ---- 退出 ***********\n");
printf("****************************************************************\n");
printf("请选择想要查看的方法路径(数字):");
}
// 深搜界面
int DepthFirstInterface(MazeType& maze, PosType start, PosType end)
{
system("cls");
printf("\n-------------- 栈 + 深搜 --------------\n");
InitMaze(maze, a);
// 如果找到一条路径
if (DepthFirstMazePath(maze, start, end))
{
printf("\n-------------------------------------------------\n");
printf("\n迷宫路径如下:\n");
printf("(其中'*'表示求解路径,'@'表示死胡同)\n");
// 输出迷宫路径
PrintMaze(maze, ROW, COL);
printf("\n\n");
printf("是否需要清除错误路径?(Yes:1 / No:0):");
int choice;
scanf("%d", &choice);
return choice;
}
// 没有通路
else
printf("\n--------------从入口到出口没有通路!--------------\n");
printf("\n\n");
return 0;
}
// 深搜求解迷宫maze中,从入口start到出口end的一条路径
int DepthFirstMazePath(MazeType& maze, PosType start, PosType end)
{
SqStack s;
SElemType e;
InitStack(s);
PosType curpos = start;
// 探索第一步
int curstep = 1;
do {
// 如果当前位置可以通过,即是未曾走到的通道块
if (Pass(maze, curpos))
{
FootPrint(maze, curpos); // 标记
e = CreateSElem(curstep,curpos, 1); // 创建元素
Push(s, e); // 加入路径
// 到达终点(出口)
if (PosEqual(curpos, end))
return 1;
curpos = NextPos(curpos, 1); // 获得下一节点
curstep++; // 继续探索
}
else
{
// 当前位置不能通过
if (!StackEmpty(s))
{
Pop(s, e);
while (e.di == 4 && !StackEmpty(s)) // 找寻了四个方向
{
// 留下不能通过的标记,并退回一步
MarkPrint(maze,e.seat);
Pop(s, e);
}
if (e.di < 4)
{
// 换一个方向探索
e.di++;
Push(s, e);
// 设定当前位置是该方向上的相邻块
curpos = NextPos(e.seat, e.di);
}
}
}
} while (!StackEmpty(s));
return 0;
}
// 广搜界面
int BreadthFirstInterface(MazeType& maze, PosType start, PosType end)
{
system("cls");
printf("\n-------------- 队列 + 广搜 --------------\n");
InitMaze(maze, a);
// 如果找到一条路径
if (BreadthFirstMazePath(maze, start, end))
{
printf("\n-------------------------------------------------\n");
printf("\n迷宫路径如下:\n");
printf("(其中'*'表示求解路径,'@'表示死胡同)\n");
// 输出迷宫路径
PrintMaze(maze, ROW, COL);
printf("\n\n");
printf("是否需要清除错误路径?(Yes:1 / No:0):");
int choice;
scanf("%d", &choice);
return choice;
}
// 没有通路
else
printf("\n--------------从入口到出口没有通路!--------------\n");
printf("\n\n");
return 0;
}
// 广搜求解迷宫maze中,从入口start到出口end的一条路径
int BreadthFirstMazePath(MazeType& maze, PosType start, PosType end)
{
int d[4][2] = { {-1,0},{0,-1},{1,0},{0,1} }; // 控制上下左右
int vis[ROW][COL]; // 访问标记数组
memset(vis, 0, sizeof(vis)); // 初始化标记数组
int head = 0, tail = 0, i = 0, ans; // 队首对位,ans当前标记
// 从起点开始
que[tail].seat = start;
que[tail++].pre = -1;
vis[start.row][start.col] = 1;
while (head < tail)
{
Strnode temp = que[head++];
MarkPrint(maze, temp.seat);
// 到达终点(出口)
if (PosEqual(temp.seat, end))
{
ans = head - 1;
Sign(maze,ans);
return 1;
}
for (i = 0; i < 4; i++)
{
int dx = temp.seat.row + d[i][0];
int dy = temp.seat.col + d[i][1];
// 未访问并且通路
if (!vis[dx][dy] && !maze.arr[dx][dy])
{
/*MarkPrint(maze, que[tail].seat);*/
que[tail].seat.row = dx;
que[tail].seat.col = dy;
que[tail++].pre = head - 1;
vis[dx][dy] = 1;
}
}
}
return 0;
}
// 广搜标记地图
void Sign(MazeType& maze, int ans)
{
if (que[ans].pre != -1)
{
Sign(maze, que[ans].pre);
}
FootPrint(maze, que[ans].seat);
return;
}
// 清除错误路径
void Clean(MazeType& maze)
{
for (int i = 1; i < ROW - 1; i++)
{
for (int j = 1; j < COL - 1; j++)
{
if (maze.arr[i][j] == 3)
maze.arr[i][j] = 0;
}
}
printf("\n-------------- 清除错误路径 --------------\n");
printf("\n-------------------------------------------------\n");
printf("\n迷宫路径如下:\n");
printf("(其中'*'表示求解路径)\n");
// 输出迷宫路径
PrintMaze(maze, ROW, COL);
}
// 结束界面
void GoodBye()
{
system("cls");
printf("ByeBye!!!\n");
Sleep(1000);
exit(0);
}