简体   繁体   English

C++中的迷宫求解算法

[英]Maze Solving Algorithm in C++

I'm writing an algorithm that finds its way through a maze by sticking to a wall and moving in this order: Down - Right - Up - Left until it finds the exit.我正在编写一种算法,它通过粘在墙上并按以下顺序移动来找到穿过迷宫的路:向下 - 右 - 上 - 左,直到找到出口。 But, sometimes, it gets stuck in an infinite loop and is unable to continue.但是,有时,它会陷入无限循环而无法继续。 I've been trying to figure out what is wrong for hours and I've had no luck.几个小时以来,我一直在试图找出问题所在,但我没有运气。 Here's the code这是代码

#include <iostream>
#include <windows.h>
const int MazeWidth = 30;
const int MazeHeight = 20;
const char MazeExit = '$';
const char Wall = '#';
const char Free = ' ';
const unsigned char SomeDude = 254;
COORD MazeExitCoords;
COORD StartingPoint;

using namespace std;
char Maze [MazeHeight][MazeWidth];




void FillDaMaze(){

    MazeExitCoords.X = MazeWidth - 20;
    MazeExitCoords.Y = 2;
    StartingPoint.X = 3;
    StartingPoint.Y = MazeHeight - 3;

    for(int i = 0; i < MazeHeight; i++){

        for(int ii = 0; ii < MazeWidth; ii++){

            if(i == 0 || i == MazeHeight - 1 || ii == 0 || ii == MazeWidth - 1){
                Maze[i][ii] = Wall;
            }
            else{
            Maze[i][ii] = Free;
            }

            if(i == MazeExitCoords.Y && ii == MazeExitCoords.X){
                    Maze[i][ii] = MazeExit;
            }
            else if(i == StartingPoint.Y && ii == StartingPoint.X){
                    Maze[i][ii] = SomeDude;
            }
        }
    }
}
void PrintDaMaze(int color){
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),color);

    for(int i = 0; i < MazeHeight; i++){

        for(int ii = 0; ii < MazeWidth;ii++){

            cout << Maze[i][ii];
        }
        cout << endl;
    }
}
void FindYourWayThroughTheMaze(){



        if(Maze[StartingPoint.Y + 1][StartingPoint.X] != Wall && Maze[StartingPoint.Y + 1][StartingPoint.X ] != SomeDude){
        StartingPoint.Y++;



        }
        else if(Maze[StartingPoint.Y][StartingPoint.X + 1] != Wall && Maze[StartingPoint.Y][StartingPoint.X + 1] != SomeDude){
            StartingPoint.X++;



        }
        else if(Maze[StartingPoint.Y - 1][StartingPoint.X] != Wall && Maze[StartingPoint.Y - 1][StartingPoint.X ] != SomeDude){
            StartingPoint.Y--;





        }
        else if(Maze[StartingPoint.Y][StartingPoint.X - 1] != Wall && Maze[StartingPoint.Y][StartingPoint.X - 1] != SomeDude){
            StartingPoint.X--;



        }


    Maze[StartingPoint.Y][StartingPoint.X] = SomeDude;

}
int main(){

FillDaMaze();
PrintDaMaze(10);
while(StartingPoint.X != MazeExitCoords.X || StartingPoint.Y != MazeExitCoords.Y){
    FindYourWayThroughTheMaze();
    system("CLS");
    PrintDaMaze(10);
    Sleep(50);
}


}

在此输入图像描述

To have a chance in solving it, you must: 要有机会解决它,你必须:

  • Create a Solve() routine and recursively call itself: 创建一个Solve()例程并递归调用自身:
    • if 1st, 2nd, 3rd, ... are true Solve has succeeded in finding a solution 如果第1,第2,第3,......都是真的, Solve已成功找到解决方案
    • if 1st, 2nd, 3rd, ... contains a false, it has to backtrack and find another way 如果第1,第2,第3,......包含假,它必须回溯并找到另一种方式
  • You need to build a buffer of places you've been to avoid infinite loops 您需要构建一个缓冲区,以避免无限循环
    • as you make moves it needs to keep tabs on it 当你做出动作时,它需要密切关注它
    • when we hit a dead end, we need to erase bad moves 当我们走到死胡同时,我们需要消除不良行为
    • we can implement the above by burning in a guess and removing it if it's wrong 我们可以通过猜测来实现上述操作,如果错误则将其删除

Here's a crude implementation based on the above concepts: 这是基于以上概念的粗略实现:

#include "stdafx.h"
#include <stdio.h>

const int MazeHeight = 9;
const int MazeWidth = 9;

char Maze[MazeHeight][MazeWidth + 1] =
{
    "# #######",
    "#   #   #",
    "# ### # #",
    "# #   # #",
    "# # # ###",
    "#   # # #",
    "# ### # #",
    "#   #   #",
    "####### #",
};

const char Wall = '#';
const char Free = ' ';
const char SomeDude = '*';

class COORD
{
public:
    int X;
    int Y;
    COORD(int x = 0, int y = 0) { X = x, Y = y; }
    COORD(const COORD &coord) { X = coord.X; Y = coord.Y; }
};

COORD StartingPoint(1, 0);
COORD EndingPoint(7, 8);

void PrintDaMaze()
{
    for (int Y = 0; Y < MazeHeight; Y++)
    {
        printf("%s\n", Maze[Y]);
    }
    printf("\n");
}

bool Solve(int X, int Y)
{
    // Make the move (if it's wrong, we will backtrack later.
    Maze[Y][X] = SomeDude;

    // If you want progressive update, uncomment these lines...
    //PrintDaMaze();
    //Sleep(50);

    // Check if we have reached our goal.
    if (X == EndingPoint.X && Y == EndingPoint.Y)
    {
        return true;
    }

    // Recursively search for our goal.
    if (X > 0 && Maze[Y][X - 1] == Free && Solve(X - 1, Y))
    {
        return true;
    }
    if (X < MazeWidth && Maze[Y][X + 1] == Free && Solve(X + 1, Y))
    {
        return true;
    }
    if (Y > 0 && Maze[Y - 1][X] == Free && Solve(X, Y - 1))
    {
        return true;
    }
    if (Y < MazeHeight && Maze[Y + 1][X] == Free && Solve(X, Y + 1))
    {
        return true;
    }

    // Otherwise we need to backtrack and find another solution.
    Maze[Y][X] = Free;

    // If you want progressive update, uncomment these lines...
    //PrintDaMaze();
    //Sleep(50);
    return false;
}

int _tmain(int argc, _TCHAR* argv[])
{
    if (Solve(StartingPoint.X, StartingPoint.Y))
    {
        PrintDaMaze();
    }
    else
    {
        printf("Damn\n");
    }

    return 0;
}

To illustrate, I have a version of the above in Javascript: 为了说明,我在Javascript中有以上版本:

 const MazeWidth = 9 const MazeHeight = 9 let Maze = [ "# #######", "# # #", "# ### # #", "# # # #", "# # # ###", "# # # #", "# ### # #", "# # #", "####### #" ].map(line => line.split('')) const Wall = '#' const Free = ' ' const SomeDude = '*' const StartingPoint = [1, 0] const EndingPoint = [7, 8] function PrintDaMaze() { //Maze.forEach(line => console.log(line.join(''))) let txt = Maze.reduce((p, c) => p += c.join('') + '\\n', '') let html = txt.replace(/[*]/g, c => '<font color=red>*</font>') $('#mazeOutput').html(html) } async function Solve(X, Y) { // Make the move (if it's wrong, we will backtrack later. Maze[Y][X] = SomeDude; // If you want progressive update, uncomment these lines... PrintDaMaze() await sleep(100) // Check if we have reached our goal. if (X == EndingPoint[0] && Y == EndingPoint[1]) { return true } // Recursively search for our goal. if (X > 0 && Maze[Y][X - 1] == Free && await Solve(X - 1, Y)) { return true } if (X < MazeWidth && Maze[Y][X + 1] == Free && await Solve(X + 1, Y)) { return true } if (Y > 0 && Maze[Y - 1][X] == Free && await Solve(X, Y - 1)) { return true } if (Y < MazeHeight && Maze[Y + 1][X] == Free && await Solve(X, Y + 1)) { return true } // Otherwise we need to backtrack and find another solution. Maze[Y][X] = Free // If you want progressive update, uncomment these lines... PrintDaMaze() await sleep(100) return false } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)) } (async function() { if (await Solve(StartingPoint[0], StartingPoint[1])) { console.log("Solved!") PrintDaMaze() } else { console.log("Cannot solve. :-(") } })() 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <pre id="mazeOutput"> </pre> 

As Luchian already posted, the algorithm (even if implemented correctly) is not suitable to find your way out of all sort of mazes: If you have some loop inside your maze, you might just end up running around this looping wall. 正如Luchian已经发布的那样,算法(即使正确实现)也不适合找到各种迷宫中的方法:如果你的迷宫中有一些环路,你可能最终会绕着这个环形墙跑来跑去。

Also, as it seems, you don't really generate a maze but rather a big field with walls at the borders and the "exit" somewhere inside it. 而且,看起来,你并没有真正产生一个迷宫,而是一个大的场地,边界有墙壁,里面有“出口”。 An algorithm, which really "sticks to a wall" will never find the exit, if the exit is not near the wall (which, again, is currently only at the borders of your "maze"). 如果出口不在墙附近(目前只在你的“迷宫”的边界处),那么真正“贴墙”的算法永远不会找到出口。

Since you're not removing the SomeDude s, ie the positions you've already been, and you're treating SomeDude the same way as a Wall , you're slowly filling up the maze with some kind of "SomeDude-Wall": You go just down until you hit the border and then go in big counterclockwise spirals around the field, leaving a trace of SomeDude s. 既然你没有删除SomeDude s,即你已经去过的位置,并且你正在以与Wall相同的方式对待SomeDude ,那么你正慢慢地用某种“SomeDude-Wall”填满迷宫:你只是向下走,直到你撞到边界,然后围绕场地逆时针旋转,留下一些SomeDude的痕迹。

Depending on your starting point and the exit, you can easily run into the situation, where all four directions are blocked, either by a "real" wall or by some previous SomeDude you left there. 根据您的出发点和出口,您可以轻松地遇到所有四个方向被阻挡的情况,无论是通过“真正的”墙还是您之前留下的一些SomeDude Then, none of the four if -Statements is executed and you just have an infinite loop (since nothing is changed inside the loop body). 然后,四个if -Statements都没有执行,你只有一个无限循环(因为循环体内没有任何改变)。

For an algorithm, wich sticks to a wall (and thus would be able to find a way out of some kinds of mazes ), I would suggest the following steps: 对于算法,它会粘在墙上(从而能够找到某种迷宫的方法 ),我建议采取以下步骤:

  • First, go into one direction, until you hit a wall. 首先,朝一个方向走,直到你碰到一堵墙。
  • Set your current direction , so that the wall is at your right side. 设置当前方向 ,使墙壁位于右侧。
  • Follow your current direction (don't forget to delete your SomeDude -trace) until either 按照您当前的指示(不要忘记删除您的SomeDude -trace)直到
    • You've found the exit. 你找到了出口。
    • There is no wall at your right side: In this case, turn right and go one step forward. 右侧没有墙:在这种情况下,向右转,向前迈出一步。
    • Or, there is a wall just in front of you. 或者,在你面前有一面墙。 In this case, turn left until the way ahead of you is free 在这种情况下, 左转 ,直到在你前面的路是免费的

This way, you ensure, that there is always "the same" wall at your right side, so you "stick" to that wall. 通过这种方式,您可以确保右侧始终存在“相同”的墙壁,因此您“坚持”到该墙。

Remember, that this algorithm cannot find exits, if the exit is inside some free space (since it always stick to a wall, the exit must also be near a wall to be found). 请记住,此算法无法找到出口,如果出口位于某个自由空间内(因为它始终贴在墙上,出口也必须靠近墙壁才能找到)。

For an algorithm which finds its way out of all possible mazes, you need to have some sort of backtracking : Remeber every point, where you have multiple choices to continue. 对于一种能够找到所有可能的迷宫的算法,你需要进行某种回溯 :记住每个点,你有多个选择继续。 Choose one way, and follow it. 选择一种方式,然后按照它。 If it's a dead-end, go back to tha last point of decision and take the next choice. 如果它是一个死胡同,请回到最后的决定点并采取下一个选择。 If no way leads to the exit, go to the previous last point and so on. 如果没有办法通往出口,请转到上一个最后一点,依此类推。 This is a recursive approach, known as "depth-first-search" in graph theory (feel free to do a bit of googling, I'm confident, you'll find a lot of material about this :) ...) 这是一种递归方法,在图论中称为“深度优先搜索”(随意做一些谷歌搜索,我很自信,你会发现很多关于这个的材料:) ...)

HTH Martin HTH马丁

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM