/*=========================================================================
# FileName: Maze.c
# Description: 迷宫求路
# author: shuizhonglin
# LastChange: 2012-05-31 20:31:42
=========================================================================*/
#include <stdio.h>
#include <stdlib.h>
/* C语言没有bool类型,自己定义一个 */
typedef int bool;
#define TRUE 1
#define FALSE 0
#define OK 1
#define FAIL 0
#define INIT_STACK_SIZE 100 //堆栈初始大小
#define INCREASE_SIZE 10 //堆栈每次空间不足时新增的空间大小
/* 记录迷宫每个位置的信息 */
typedef struct {
int x; //横坐标
int y; //纵坐标
int di; //di=0表示东南西北四条路都未走过
//di=1表示要走南路,其他三路未走过
//di=2表示要走西路,南路已走过且不通,其他两路未走过
//di=3表示要走北路,西、南路已走过且不通,东路未走过
//di=4表示要走东路,其他三路已走过且不通
}SElem;
/* 堆栈结构体,堆栈存储指向SElem结构体的指针 */
typedef struct {
SElem **base;
SElem **top;
int stacksize;
}SqStack;
/* 用数组画迷宫图样 */
char Maze[10][10] = {
{'#','#','#','#','#','#','#','#','#','#'},
{'#',' ',' ','#',' ',' ',' ','#',' ','#'},
{'#','#',' ',' ',' ',' ',' ','#',' ','#'},
{'#',' ',' ',' ',' ','#',' ',' ',' ','#'},
{'#',' ','#','#','#',' ',' ',' ',' ','#'},
{'#',' ','#',' ','#',' ',' ','#',' ','#'},
{' ',' ','#',' ',' ',' ','#',' ',' ','#'},
{'#',' ','#','#','#','#','#','#',' ','#'},
{'#','#',' ',' ',' ',' ',' ',' ',' ','#'},
{'#','#',' ','#','#','#','#','#','#','#'}
};
SElem * pMaze[10][10]; //定义100个指向SElem结构体的指针
SqStack PrintOnRoad; //记录所求路径
/* 创建并初始化100个SElem结构体,并用pMaze指针数组分别指向它们 */
void InitMaze() {
int i,j;
for(i=0; i<10; ++i)
for(j=0; j<10; ++j) {
pMaze[i][j] = (SElem *)malloc( sizeof(SElem) );
pMaze[i][j]->x = i;
pMaze[i][j]->y = j;
pMaze[i][j]->di = 0;
}
}
/* 判断(xValue,yValue)对应的SElem的指针是否在堆栈S中 */
bool IsInStack(const SqStack *S,int xValue,int yValue) {
SElem **top = S->top;
if(S->top == S->base) return FALSE;
do{
top--;
if((*top)->x == xValue && (*top)->y == yValue) return TRUE;
} while(top != S->base);
return FALSE;
}
/* 判断位置(x,y)是否可走 */
bool OnePass(int x,int y) {
if( IsInStack( &PrintOnRoad,x,y ) ) return FALSE; //是否在PrintOnRoad堆栈中
else if (pMaze[x][y]->di == 4) return FALSE; //是否之前已走过且已知不通
else if (Maze[x][y] == '#') return FALSE; //是否是墙
else return TRUE; //可走
}
/* 判断位置(x,y)四周是否有路有走 */
bool Pass(int x,int y) {
if(!OnePass(x,y)) return FALSE; //(x,y)本身就不能走
pMaze[x][y]->di++;
if(x+1 <= 9)
if(OnePass(x+1,y)) return TRUE; //南路是否可走
pMaze[x][y]->di++;
if(y-1 >= 0)
if(OnePass(x,y-1)) return TRUE; //西路是否可走
pMaze[x][y]->di++;
if(x-1 >= 0)
if(OnePass(x-1,y)) return TRUE; //北路是否可走
pMaze[x][y]->di++;
if(y+1 <= 9)
if(OnePass(x,y+1)) return TRUE; //东路是不可走
return FALSE; //四周都走不通
}
/* 初始化堆栈 */
bool InitStack( SqStack *S) {
S->base = (SElem **)malloc(INIT_STACK_SIZE * sizeof(SElem *));
if(!S->base) return FAIL;
S->top = S->base;
S->stacksize = INIT_STACK_SIZE;
return OK;
}
/* 将e所指向的SElem *类型的指针入栈 */
bool Push(SqStack *S, SElem **e) {
if(S->top - S->base >= S->stacksize) {
S->base = (SElem **)realloc(S->base, (S->stacksize + INCREASE_SIZE) * sizeof(SElem *));
if(!S->base) return FAIL;
S->top = S->base + S->stacksize;
S->stacksize += INCREASE_SIZE;
}
*(S->top) = *e;
S->top++;
return OK;
}
/* 出栈,并用e所指向的空间存放所弹出的表项 */
bool Pop(SqStack *S,SElem **e) {
if(S->top == S->base) return FAIL;
S->top--;
*e = *(S->top);
return OK;
}
/* 判断S堆栈是否为空 */
bool IsEmpty(const SqStack *S) {
if(S->top == S->base) return TRUE;
else return FALSE;
}
/* 判断(x,y)是否是出口,(xBegin,yBegin)是入口坐标 */
bool IsTheExit(int x,int y,int xBegin,int yBegin) {
if( x == xBegin && y == yBegin) return FALSE;
if((x == 9 || x == 0 || y == 9 || y == 0) && Maze[x][y] == ' ' ) return TRUE;
else return FALSE;
}
/* 给定入口坐标(xBegin,yBegin),求出口坐标(*xEnd,*yEnd),并将路径存入堆栈PrintOnRoad中 */
bool MazePath(int xBegin,int yBegin,int *xEnd,int *yEnd) {
InitStack( &PrintOnRoad );
InitMaze();
SElem *e = (SElem *)malloc( sizeof(SElem) );
int x = xBegin, y = yBegin;
do {
/* 如果是出口则入栈并返回坐标值 */
if( IsTheExit(x,y,xBegin,yBegin) ) {
*xEnd = x;
*yEnd = y;
Push( &PrintOnRoad, &(pMaze[x][y]) );
return OK;
}
/* 如果(x,y)四周有路可走 */
if( Pass(x,y) ) {
/* 保存该路径 */
Push( &PrintOnRoad, &(pMaze[x][y]) );
/* 判断该走哪条路 */
switch( pMaze[x][y]->di ) {
case 1 : ++x; break; //走南路
case 2 : --y; break; //走西路
case 3 : --x; break; //走北路
case 4 : ++y; break; //走东路
default: return FAIL;
}
}
/* 如果(x,y)四周不通 */
else {
/* 是否无路可退 */
if( !IsEmpty(&PrintOnRoad) ) {
/* 向后退,退到四周还有路未走过的地方*/
Pop( &PrintOnRoad, &e );
while(e->di == 4 && !IsEmpty(&PrintOnRoad)) {
Pop( &PrintOnRoad, &e );
}
/*重新选择一条路 */
if(e->di < 4) {
x = e->x;
y = e->y;
e->di++;
switch( e->di ) {
case 1 : ++x; break; //走南路
case 2 : --y; break; //走西路
case 3 : --x; break; //走北路
case 4 : ++y; break; //走东路
default: return FAIL;
}
/* 重新保存该路径 */
Push( &PrintOnRoad, &e );
}
}
}
}while( !IsEmpty(&PrintOnRoad) ); //是否没有出口,或入口有误
return FAIL;
}
/* 打印迷宫 */
void PrintMaze() {
SElem **base = PrintOnRoad.base;
int i,j;
int k = '0';
/* 如果PrintOnRoad保存有路径,则用0~9,A~Z,a~...来表示路径顺序,并打印出来 */
while(base != PrintOnRoad.top) {
if(k <= '9')
Maze[(*base)->x][(*base)->y] = k;
else if(k <= 'S')
Maze[(*base)->x][(*base)->y] = k + 7;
else
Maze[(*base)->x][(*base)->y] = k + 39;
base++;
k++;
}
for(i=0; i<10; ++i) {
printf( " " );
for(j=0; j<10; ++j) {
printf("%2c", Maze[i][j]);
}
putchar('\n');
}
}
int main( int argc, const char *argv[] )
{
int xBegin = 6, yBegin = 0, xEnd = 0,yEnd = 0;
printf( "The maze is as follows,and we assume the entrance is at (6,0).\n\n" );
PrintMaze();
if( MazePath(xBegin,yBegin,&xEnd,&yEnd) )
printf( "\nThe exit locate at : (%d,%d), and the route is as the follows:\n\n",xEnd,yEnd);
else printf( "There is not exit at all or the entrance is wrong!\n" );
PrintMaze();
return 0;
}