迷宮小遊戲最短路徑的選擇

我們主要通過一個棧來實現一個迷宮,用壓棧來記錄路徑

  • 迷宮我們定義66大小的迷宮這個走法原則上是可以無限的,但是我們定義的我們必須需要考錄這個我們用棧實現這個迷宮需要考慮的問題,首先來說棧是有大小的啊,所以在沒有回溯的前提下,這個迷宮一直不走老路線也就是壓棧,這個對於66迷宮來說綽綽有餘了
  • 我們定義的這個迷宮在計算x,y座標時候的邏輯是x向下的,y向右的爲正
  • 時候越界以及我們怎麼樣纔算是走到出口了,就要看我們是否走到定義出口的呢一行或或者一列了,我把出口定義最右邊,那麼如果在迷宮一個出口的前提下,碰到最後一列也就是肯定走出來了,即(X,6)
  • 用二位數組定義迷宮以後,一個正常的迷宮是由區分障礙物的和什麼不是障礙物,我們在計算他那條路徑是最短路徑時候,還在加一個區分條件,只要能區分原理其實都一樣,拿什麼區分也·都一樣。
  • 判斷能不能走憑什麼判斷了,不能憑空判斷的,可以定義兩個數一個0,一個1,碰見1,就要尋找其他路徑,0就表示沒有障礙物了。
  • 看一下代碼吧
#pragma once

#include <stdio.h>
#include <string.h>

// 用全局變量,但不是最好的方式
#define ROWS (6)
#define COLS (6)

// 用來保存迷宮中的座標
// 座標方向和平時不太一樣,x 朝下,y 朝右
typedef struct {
	int	x;
	int y;
}	Position;

// 棧代碼
#include <assert.h>

typedef Position StackDataType;

#define	MAX_SIZE	(100)

typedef struct Stack {
	StackDataType	array[MAX_SIZE];
	int				top;	// 表示當前個數
}	Stack;

// 初始化/銷燬
// 增(只能從棧頂)/刪(只能刪除棧頂)/查(只能查看棧頂元素)
// 個數 / 是否空 / 是否滿
// 增 -> 順序表的尾插
// 刪 -> 順序表的尾刪

void StackInit(Stack *pStack)
{
	pStack->top = 0;
}

void StackDestroy(Stack *pStack)
{
	pStack->top = 0;
}

void StackPush(Stack *pStack, StackDataType data)
{
	assert(pStack->top < MAX_SIZE);

	pStack->array[pStack->top++] = data;
}

void StackPop(Stack *pStack)
{
	assert(pStack->top > 0);

	pStack->top--;
}

StackDataType StackTop(const Stack *pStack)
{
	assert(pStack->top > 0);

	return pStack->array[pStack->top - 1];
}

int StackSize(const Stack *pStack)
{
	return pStack->top;
}

int StackFull(const Stack *pStack)
{
	return pStack->top >= MAX_SIZE;
}

int StackEmpty(const Stack *pStack)
{
	return pStack->top <= 0;
}

void StackCopy(Stack *pDest, Stack *pSrc)
{
	memcpy(pDest->array, pSrc->array, sizeof(StackDataType)* pSrc->top);
	pDest->top = pSrc->top;
}

// 棧代碼結束


int gMaze[ROWS][COLS] = {
	{ 0, 0, 0, 0, 0, 0 },
	{ 0, 0, 1, 1, 1, 0 },
	{ 0, 0, 1, 0, 1, 0 },
	{ 0, 0, 1, 0, 1, 0 },
	{ 0, 0, 1, 1, 1, 1 },
	{ 0, 0, 1, 0, 0, 0 }
};

// 入口點
Position gEntry = { 5, 2 };

// 判斷是否走到出口,最後一列都是出口
int IsExit(Position pos)
{
	if (pos.y == COLS - 1) {
		return 1;
	}
	else {
		return 0;
	}
}

// 判定是否可以走
// 沒有越界 && 值是 1
int CanPass(Position pos)
{
	if (pos.x >= ROWS) {
		return 0;
	}

	if (pos.y >= COLS) {
		return 0;
	}

	return gMaze[pos.x][pos.y] == 1;
}

void PrintPath(Stack *pStack)
{
	Position at;
	for (int i = 0; i < pStack->top; i++) {
		at = pStack->array[i];
		printf("x = %d, y = %d\n", at.x, at.y);
	}
}

Stack path;
Stack min;


void PrintMaze()
{
	for (int i = 0; i < ROWS; i++) {
		for (int j = 0; j < COLS; j++) {
			if (gMaze[i][j] == 0) {
				printf("█");
			}
			else if (gMaze[i][j] == 1) {
				printf("  ");
			}
			else if (gMaze[i][j] == 2) {
				printf("⊕");
			}
		}
		printf("\n");
	}

	printf("\n\n");
}


void RunMazeRec(Position at)
{
	Position next;
	StackPush(&path, at);

	// 一進來標記這個我走過了
	gMaze[at.x][at.y] = 2;
	PrintMaze();

	// 爲什麼不需要在棧裏記錄,直接利用調用棧回溯

	if (IsExit(at)) {
		// 如果當前路徑 (path)小於之前的最小路徑 (min),則當前路徑是最短
		if (StackEmpty(&min) || StackSize(&path) < StackSize(&min)) {
			StackCopy(&min, &path);
		}

		PrintPath(&path);
		printf("============================\n");
		//printf("找到出口: x = %d, y = %d\n", at.x, at.y);
		
		// 重新置爲 1
		gMaze[at.x][at.y] = 1;

		StackPop(&path);
		return;	// 會發生回溯
	}

	// 根據 左 -> 上 -> 右 -> 下 來嘗試
	next.x = at.x;
	next.y = at.y - 1;
	if (CanPass(next)) {
		RunMazeRec(next);
		PrintMaze();
	}

	next.x = at.x - 1;
	next.y = at.y;
	if (CanPass(next)) {
		RunMazeRec(next);
		PrintMaze();
	}

	next.x = at.x;
	next.y = at.y + 1;
	if (CanPass(next)) {
		RunMazeRec(next);
		PrintMaze();
	}

	next.x = at.x + 1;
	next.y = at.y;
	if (CanPass(next)) {
		RunMazeRec(next);
		PrintMaze();
	}

	// 重新置爲 1
	gMaze[at.x][at.y] = 1;

	StackPop(&path);
	return;	// 回溯
}

void RunMaze()
{
	// 需要一個棧,實現回溯
	Stack stack;
	StackInit(&stack);
	Position at;
	Position next;
	at.x = gEntry.x;
	at.y = gEntry.y;

	while (1) {
		// 標記當前位置我已經走過了
		gMaze[at.x][at.y] = 2;

		// 在棧裏記錄當前位置,爲了以後回溯做準備
		StackPush(&stack, at);

		if (IsExit(at)) {
			//printf("x = %d, y = %d\n", at.x, at.y);
			PrintPath(&stack);
			return;
		}

		// 根據 左 -> 上 -> 右 -> 下 來嘗試
		next.x = at.x;
		next.y = at.y - 1;
		if (CanPass(next)) {
			at.x = next.x;
			at.y = next.y;
			continue;
		}

		next.x = at.x - 1;
		next.y = at.y;
		if (CanPass(next)) {
			at.x = next.x;
			at.y = next.y;
			continue;
		}

		next.x = at.x;
		next.y = at.y + 1;
		if (CanPass(next)) {
			at.x = next.x;
			at.y = next.y;
			continue;
		}

		next.x = at.x + 1;
		next.y = at.y;
		if (CanPass(next)) {
			at.x = next.x;
			at.y = next.y;
			continue;
		}

		//回溯
		StackPop(&stack);	// 這裏 pop 的是當前的 at
		if (StackEmpty(&stack)) {
			printf("沒有出口\n");
			return;
		}

		at = StackTop(&stack);
		StackPop(&stack);
	}
}


void TestRunMaze1()
{
	
	StackInit(&path);
	StackInit(&min);
	RunMazeRec(gEntry);

	printf("最短路徑長度是 %d\n", StackSize(&min));
}

int main()
{

	TestRunMaze1();
	return 0;
}

看一下末尾結果,看一下我們的迷宮有沒有按照正常迷宮的思維邏輯走的,遇到障礙物就不能走,遇到出口就出了,看一下最短路徑,與我們思考的最短路徑一樣不一樣
在這裏插入圖片描述
在這裏插入圖片描述
回溯過程和最短路徑都沒有什麼問題,就這樣了啊。。。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章