深度優先搜索走迷宮+SDL可視化

https://apriljia.com/2018/07/18/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2%E8%B5%B0%E8%BF%B7%E5%AE%ABsdl%E5%8F%AF%E8%A7%86%E5%8C%96/

走迷宮問題是指在給定地圖中,從尋找一條從起點(設爲左上角)到終點(設爲右下角)的路徑的問題。假設迷宮形狀如下:

 

其中一個方格表示一個位置,從一個位置有上下左右四種走法,但每個位置的四個方向上可能會有牆壁存在,牆壁用黑線表示,無法通過,要求尋找一條從左上角到右下角的路線。

深度優先搜索可以很好地解決迷宮問題,深度優先搜索的思路是從一個位置開始,嘗試向某個能到達的方向走一步,在從新的位置重複這個步驟,要注意走過的位置就不要再走了,直到走到一個無路可走的位置時返回上一個位置,嘗試上一個位置的另一方向。可以看出深度優先搜索就相當於時枚舉出了各種可能的路徑,像個鐵頭娃一路走到底,沒路了退一點再朝另一個方向一路走到底,直到把出口路徑試出來,因此深度優先搜索得到的路徑往往不是最優的。

以下是使用深度優先搜索走迷宮的練習,後面附帶一個用SDL對走迷宮進行可視化的過程,程序會隨機生成迷宮地圖,並嘗試走迷宮,但是畢竟是隨機生成不保證一定有解,若存在路徑則會用紅線標註出來。SDL是一個簡單的圖形庫,感興趣的可以瞭解一下。效果圖如下:

代碼很簡單,直接貼上,迷宮大小可以在代碼裏改變。maze.h和maze.cpp實現生成迷宮和走迷宮功能,將maze.cpp中的main函數註釋去掉即可運行,後面的SDLtest是可視化部分,需要配置SDL庫。

maze.h

#pragma once
#include <iostream>
#include <vector>
#include <random>
#define MX 5
#define MY 5

struct point {
	int i;
	int j;
	point() = default;
	point(int ii, int jj) :i(ii), j(jj) {}
};
class cell {
public:
	cell() = default;
	cell(int ttop, int tdown, int tright, int tleft) :top(ttop), down(tdown), right(tright), left(tleft), used(false) {}
	bool used;
	int top, down, right, left;
};

class maze {
public:
	maze() {
		for (int i = 0; i<MY; i++) {
			std::vector<cell> cell_row(MX);
			cells.push_back(cell_row);
		}
	}
	std::random_device rd;
	int generate();
	void print() const;
	void draw() const;
	//#ifdef whole
	void render() const;
	void renderroute() const;
	//	#endif
	int find(int i, int j);
	point getroute(int step) {
		if (!route.empty())
			return route[step];
	}
	bool routeempty() {
		return route.empty();
	}
	void showroute();
private:
	std::vector <std::vector<cell> > cells;
	std::vector <point> route;
	int rand1();
};

 

maze.cpp

#include "stdafx.h"
#include "maze.h"


void maze::showroute(){
	if (route.empty()){
		std::cout<<"empty route";
		return;
	}
	std::vector<point>::iterator it;
	for (it=route.begin();it !=route.end();it++){
		std::cout<<"("<<it->i<<","<<it->j<<")";
	}
}
int maze::generate(){
	
	for (int j=0;j<MX;j++){
		cells[0][j].top=0;
	}
	for (int i=0;i<MY;i++){
		cells[i][0].left=0;
	}
	for (int i=0;i<MY;i++){
		for (int j=0;j<MX;j++){
			cells[i][j].right=rand1();
			cells[i][j].down=rand1();
			if (j>=1){
			cells[i][j].left=cells[i][j-1].right;
			}
			if (i>=1){
			cells[i][j].top=cells[i-1][j].down;
			}
		}
	
	}
	for (int j=0;j<MX;j++){
		cells[MY-1][j].down=0;
	}
	for (int i=0;i<MY;i++){
		cells[i][MX-1].right=0;
	}
	return 1;
}
int maze::rand1(){
	return rd()%2;
}
void maze::print() const{
	for (int i=0;i<MY;i++){
		for (int j=0;j<MX;j++){	std::cout<<cells[i][j].top<<cells[i][j].down<<cells[i][j].left<<cells[i][j].right<<" ";	
		}
		std::cout<<std::endl;
	}
}
void maze::draw() const{
	for (int i=0;i<MY;i++){
		for (int j=0;j<MX;j++){
			std::cout<<" "<<cells[i][j].top<<"  ";
		}
		std::cout<<std::endl;
		for (int j=0;j<MX;j++){
			std::cout<<cells[i][j].left<<" "<<cells[i][j].right<<" ";
		}
		std::cout<<std::endl;
		for (int j=0;j<MX;j++){
			std::cout<<" "<<cells[i][j].down<<"  ";
		}
		std::cout<<std::endl;
	}
}

int maze::find(int i,int j){
	route.clear();
	cells[i][j].used=1;
	if ((i==MY-1)&&(j==MX-1)) {
		route.insert(route.begin(),point(i,j));
		cells[i][j].used=0;
		return 1;
		}
	if (cells[i][j].top==1&&cells[i-1][j].used==0){
		if (find(i-1,j)==1) {
			route.insert(route.begin(),point(i,j));
			cells[i][j].used=0;
			return 1;
			}
	}
	if (cells[i][j].down==1&&cells[i+1][j].used==0){
		if (find(i+1,j)==1) {
			route.insert(route.begin(),point(i,j));
			cells[i][j].used=0;
			return 1;
			}
	}
	if (cells[i][j].left==1&&cells[i][j-1].used==0){
		if (find(i,j-1)==1) {
			route.insert(route.begin(),point(i,j));
			cells[i][j].used=0;
			return 1;
			}
	}
	if (cells[i][j].right==1&&cells[i][j+1].used==0){
		if (find(i,j+1)==1) {
			route.insert(route.begin(),point(i,j));
			cells[i][j].used=0;
			return 1;
			}
	}
	cells[i][j].used=0;
	return 0;
}
/*
int main() {
	maze m;
	m.generate();
	m.print();
	m.draw();
	int a, b;
	if (m.find(0, 0)) {
		m.showroute();
		std::cout << "found" << std::endl;
	}
	else std::cout << "not found" << std::endl;

	while (1) {
		std::cin >> a >> b;
		std::cout << a << "," << b << std::endl;
		if (m.find(a, b)) {
			m.showroute();
			std::cout << "found" << std::endl;
		}
		else std::cout << "not found" << std::endl;
	}

}
*/

 

SDLtest

#include "stdafx.h"
#define SDL_MAIN_HANDLED
#include <iostream>
#include <vector>
#include "maze.h"
#include <SDL.h>
#define bx 66
#define by 66
#define gap 66
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;

void maze::render() const {
	for (int j = 0; j<MX; j++) {
		if (cells[0][j].top == 0) {
			SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
		}
		else {
			SDL_SetRenderDrawColor(renderer, 175, 175, 225, 0);
		}
		SDL_RenderDrawLine(renderer, bx + j*gap, by, bx + (j + 1)*gap, by);
	}
	for (int i = 0; i<MY; i++) {
		if (cells[i][0].left == 0) {
			SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
		}
		else {
			SDL_SetRenderDrawColor(renderer, 175, 175, 225, 0);
		} SDL_RenderDrawLine(renderer, bx, by + i*gap, bx, by + (i + 1)*gap);
	}

	for (int i = 0; i<MY; i++) {
		for (int j = 0; j<MX; j++) {
			if (cells[i][j].down == 0) {
				SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
			}
			else {
				SDL_SetRenderDrawColor(renderer, 175, 175, 225, 0);
			} SDL_RenderDrawLine(renderer, bx + j*gap, by + (i + 1)*gap, bx + (j + 1)*gap, by + (i + 1)*gap);
			if (cells[i][j].right == 0) {
				SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
			}
			else {
				SDL_SetRenderDrawColor(renderer, 175, 175, 225, 0);
			} SDL_RenderDrawLine(renderer, bx + (j + 1)*gap, by + i*gap, bx + (j + 1)*gap, by + (i + 1)*gap);
		}
	}
}
void maze::renderroute() const {
	if (route.empty()) {
		return;
	}
	int x1, x2, y1, y2;
	for (auto it = route.begin(); it != (route.end() - 1); it++) {
		y1 = (it->i)*gap + 0.5*gap + by;
		y2 = ((it + 1)->i)*gap + 0.5*gap + by;
		x1 = (it->j)*gap + 0.5*gap + bx;
		x2 = ((it + 1)->j)*gap + 0.5*gap + bx;
		SDL_RenderDrawLine(renderer, x1, y1, x2, y2);
	}
}
void init() {
	SDL_Init(SDL_INIT_VIDEO);
	SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
	window = SDL_CreateWindow("MAZE", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 480, 480, SDL_WINDOW_SHOWN);
	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED || SDL_RENDERER_PRESENTVSYNC);
}
void close() {
	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	SDL_Quit();
}
int main() {
	maze m;
	m.generate();
	m.find(0, 0);
	init();
	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION,
		"PROMPT", "Click on the screen to create a new maze", NULL);
	SDL_SetRenderDrawColor(renderer, 100, 100, 200, 0);
	SDL_RenderClear(renderer);
	bool quit = false;
	SDL_Event e;
	while (!quit) {
		SDL_SetRenderDrawColor(renderer, 155, 155, 200, 0);
		SDL_RenderClear(renderer);
		while (SDL_PollEvent(&e)) {
			switch (e.type) {
			case SDL_QUIT: quit = true;
				break;
			case SDL_MOUSEBUTTONUP:
				m.generate();
				if (m.find(0, 0)) {
					SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION,
						"result", "Found it.", NULL);
				}
				else {
					SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION,
						"result", "Cannot find the route.please try more.", NULL);

				}
				break;
			default:
				break;
			}


		}

		SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
		m.render();
		SDL_SetRenderDrawColor(renderer, 250, 0, 0, 0);
		m.renderroute();
		SDL_RenderPresent(renderer);


	}
	return 0;
}

 

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