走迷宮問題是指在給定地圖中,從尋找一條從起點(設爲左上角)到終點(設爲右下角)的路徑的問題。假設迷宮形狀如下:
其中一個方格表示一個位置,從一個位置有上下左右四種走法,但每個位置的四個方向上可能會有牆壁存在,牆壁用黑線表示,無法通過,要求尋找一條從左上角到右下角的路線。
深度優先搜索可以很好地解決迷宮問題,深度優先搜索的思路是從一個位置開始,嘗試向某個能到達的方向走一步,在從新的位置重複這個步驟,要注意走過的位置就不要再走了,直到走到一個無路可走的位置時返回上一個位置,嘗試上一個位置的另一方向。可以看出深度優先搜索就相當於時枚舉出了各種可能的路徑,像個鐵頭娃一路走到底,沒路了退一點再朝另一個方向一路走到底,直到把出口路徑試出來,因此深度優先搜索得到的路徑往往不是最優的。
以下是使用深度優先搜索走迷宮的練習,後面附帶一個用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;
}