一、程序设计思路
1、题目:应用栈实现迷宫游戏
要求:以书中3.2.4节迷宫求解为基础实现迷宫游戏,游戏运行时显示一个迷宫地图(迷宫内容结构可以参照书中图片,也可以自己编写),玩家从地图左上角的入口处进入迷宫,从右下角出口离开迷宫。玩家不能穿墙而过。
2、解决思路
采用“穷举求解”方法,需要用到栈,从入口开始,往四个方向走,依次将走过的坐标点–入栈;如果走到“死路”,就出栈;一一循环;最后栈中保存的就是出迷宫的路线。
3、算法描述
求迷宫中一条从入口到出口的路径的算法可简单描述如下:
设当前位置的初值为入口位置;
do{
若当前位置可通,
则{
将当前位置压至栈顶; //纳入路径
如果当前位置是出口位置,则结束;
否则切换当前位置的东邻方块为新的当前位置;
}
否则,
若栈不为空且栈顶位置尚有其他方向未经探索,
则设定新的当前位置为沿顺时针方向旋转找到的栈顶位置的下一相邻方块;
若栈不为空且栈顶位置的四周均不可通,
则{
删去栈顶位置;
若栈不为空,则重新测试新的栈顶位置,
直至找到一个可通的相邻块或者出栈至栈空;
}
}while(栈不为空);
二、程序源代码
栈部分
#include "stdafx.h" //包含标准C的头文件,stdio.h,string.h等
#include <malloc.h> //动态储存分配函数头文件,用于栈的储存空间分配
#include <stdlib.h> //standard library标准库头文件
typedef int DirectiveType; //下一个通道方向
#define RANGE 100 //迷宫大小
#define STACK_INIT_SIZE 100 //定义栈的初始大小
#define STACKINCREMENT 10 //定义栈的储存增量,在栈长度越界时
typedef int DirectiveType; //下一个通道方向
#define RANGE 100 //迷宫大小
#define ROW 10 //迷宫的行数
#define COL 10 //迷宫的列数
typedef struct
{
int m, n;
int arr[RANGE][RANGE]; //迷宫数组
}MazeType; //迷宫的类型
typedef struct
{
int row; //迷宫中的行
int col; //迷宫中的列
}PosType; //坐标(row,col)
typedef struct
{
int step; //当前位置在路径上的"序号"
PosType seat; //当前的坐标位置
DirectiveType di; //往下一个坐标位置的方向
}SElemType; //栈的元素类型
typedef struct
{
SElemType *base; //栈底
SElemType *top; //栈顶
int stacksize; //栈的大小
}SqStack; //定义栈
bool InitStack(SqStack &s)
{ //栈的初始化
s.base = (SElemType
*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!s.base)
{
exit(-2);
}
s.top = s.base;
s.stacksize = STACK_INIT_SIZE;
return true;
}
bool GetTop(SqStack s, SElemType &e)
//当栈s不为空时,返回栈顶e
{
if (s.top == s.base)
return false;
e = *(s.top - 1);
return true;
}
bool Push(SqStack &s, SElemType e)
//在栈s中插入元素e,入栈
{
if (s.top - s.base >=
s.stacksize)
{ //若栈满,追加存储空间
s.base = (SElemType
*)realloc(s.base, (s.stacksize + STACKINCREMENT) * sizeof(SElemType));
if (!s.base)
exit(-2);
s.top = s.base + s.stacksize;
s.stacksize += STACKINCREMENT;
}
*s.top++ = e;
return true;
}
bool Pop(SqStack &s, SElemType &e) //在栈s中删除栈顶,出栈
{
if (s.top == s.base) //当栈空时
return false; //返回错误
e = *--s.top; //e指向新栈顶
return true;
}
bool StackEmpty(SqStack s)
//栈判空
{
return s.base == s.top;
}
bool DestoryStack(SqStack &s)
//销毁栈
{
free(&s); //释放栈s的储存空间
return true;
}
迷宫部分
bool InitMaze(MazeType &maze, int a[ROW][COL], int row, int col) //初始化迷宫
{
int i, j; //设置迷宫maze的初值,包括加上边缘一圈的值
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 true;
}
bool Pass(MazeType maze, PosType curpos)
{
//判断当前节点是否通过
if (maze.arr[curpos.row][curpos.col]
== 0) //当节点为0时返回真
return true;
else
return false;
}
bool FootPrint(MazeType &maze, PosType curpos)
{
//留下足迹
maze.arr[curpos.row][curpos.col]
= 2; //走过且走得通
return true;
}
bool MarkPrint(MazeType &maze, PosType curpos)
{
//留下不能通过的标记
maze.arr[curpos.row][curpos.col]
= 3; //走过但走不通
return true;
}
SElemType CreateSElem(int step, PosType pos, int di)
{
//创建元素e
SElemType e;
e.step = step;
e.seat = pos;
e.di = di;
return e;
}
PosType NextPos(PosType curpos, DirectiveType di) //curpos当前位置
{
//返回当前节点的下一节点
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;
}
bool PosEqual(PosType pos1, PosType pos2)
{
//判断是不是出口
if (pos1.row == pos2.row
&& pos1.col == pos2.col)
return true;
else
return false;
}
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");
}
}
bool MazePath(MazeType &maze, PosType start, PosType end)
{ //求解迷宫maze中,从入口start到出口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 true;
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 false;
}
主函数部分
int main() //迷宫求解主函数
{
int i, j;
PosType start, end; //开始,终点坐标
MazeType maze;
int a[ROW][COL] = { //原始迷宫,其中'1'表示墙,'0'表示通道
{ 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,1,0,0,0,0,0,0,0,1 },
{ 1,1,1,1,1,1,1,1,1,1 }
};
printf("\n-------------------------------------------------\n");
printf("\n原始迷宫如下:\n");
printf("(其中'1'表示墙,'0'表示通道)\n");
for (i = 0; i<10; i++) //双重循环输出原始迷宫
{
for (j = 0; j<10; j++)
{
printf("%d ",
a[i][j]);
}
printf("\n");
}
InitMaze(maze, a, ROW, COL); //初始化迷宫
start.row = 1; //给定迷宫起点坐标
start.col = 1; //(1,1)
end.row = 8; //给定迷宫终点坐标
end.col = 8; //(8,8)
if (MazePath(maze, start,
end)) //如果找到一条路径
{
printf("\n-------------------------------------------------\n");
printf("\n穷举法求解迷宫路径如下:\n");
printf("(其中'*'表示求解路径,'@'表示死胡同)\n");
PrintMaze(maze, ROW,
COL); //输出迷宫路径
}
else //否则,没有通路
printf("\n---------从入口到出口没有通路!-----\n");
}
三、运行结果
附:源代码链接迷宫求解源代码