C語言 棧 資料結構 迷宮求解(附完整程式碼)
阿新 • • 發佈:2018-12-21
一、程式設計思路
1、題目:應用棧實現迷宮遊戲
要求:以書中3.2.4節迷宮求解為基礎實現迷宮遊戲,遊戲執行時顯示一個迷宮地圖(迷宮內容結構可以參照書中圖片,也可以自己編寫),玩家從地圖左上角的入口處進入迷宮,從右下角出口離開迷宮。玩家不能穿牆而過。本題目需在作業中完成程式設計思路、程式原始碼和遊戲視訊三部分。其中,程式設計思路部分以文字敘述為主,詳細闡述迷宮和玩家在迷宮中移動的基本操作;程式原始碼部分要求給出完整的程式程式碼,並對每一句程式碼加註釋,學生採用C/C++程式語言來完成;大作業最後需要學生錄製一段遊戲視訊證明執行正常。
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");
}
三、執行結果