[英]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: 要有机会解决它,你必须:
Solve()
routine and recursively call itself: Solve()
例程并递归调用自身:
Solve
has succeeded in finding a solution Solve
已成功找到解决方案 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: 对于算法,它会粘在墙上(从而能够找到某种迷宫的方法 ),我建议采取以下步骤:
SomeDude
-trace) until either SomeDude
-trace)直到
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.