简体   繁体   English

基于堆栈的迷宫算法背后的逻辑

[英]Logic behind a stack-based maze algorithm

I'm fairly new to C++ programming, and I'm working on a maze solving algorithm. 我是C ++编程的新手,我正在研究一种迷宫求解算法。 I need to use an explicit stack to keep track of the moves of done, no recursion. 我需要使用显式堆栈来跟踪完成的移动,没有递归。

Basically I'm responsible for the "Solver" algorithm, I can check to see if a move is available or blocked and do it, or I can undo a move. 基本上我负责“解算器”算法,我可以查看移动是否可用或阻止并执行此操作,或者我可以撤消移动。 The moves are left, right and forward. 这些动作是左,右和前进。 There is already code that takes care of drawing and updating the maze. 已经有代码负责绘制和更新迷宫。

What I could use is just some help understanding the basic algorithm for traversing the maze. 我可以使用的只是帮助理解穿越迷宫的基本算法。 I've looked at a lot of different programs for doing it, but I just can't seem to figure it out. 我已经看了很多不同的程序,但我似乎无法弄明白。 The maze I'm solving is generated randomly, with no loops. 我正在解决的迷宫是随机生成的,没有循环。

Here is what I can't wrap my mind around: say I have a straight section of wall, but there's a branch coming out of the wall as well. 这是我无法理解的事情:说我有一段直的墙,但也有一个分支从墙上出来。 Say I decide to go down this other branch, but eventually it leads to a dead end. 说我决定离开这个分支,但最终导致死路一条。 I've pushed all the moves I've done onto the stack. 我把我所做的所有动作都推到了堆栈上。 Basically, how do I know how many moves I need to pop off the stack to know I'm back at the original junction, so I can take the other branch instead of the blocked one? 基本上,我怎么知道我需要从堆栈中弹出多少动作才能知道我回到原来的交叉点,所以我可以拿另一个分支而不是阻塞的分支?

Thanks for any help! 谢谢你的帮助!

Here is what I can't wrap my mind around: Say I decide to go down this other branch, but eventually it leads to a dead end. 这是我无法理解的事情:说我决定走下这个分支,但最终导致死路一条。 I've pushed all the moves I've done onto the stack. 我把我所做的所有动作都推到了堆栈上。 Basically, how do I know how many moves I need to pop off the stack to know I'm back at the original junction, so I can take the other branch instead of the blocked one? 基本上,我怎么知道我需要从堆栈中弹出多少动作才能知道我回到原来的交叉点,所以我可以拿另一个分支而不是阻塞的分支?

All you need to do is always make the choices in a predefined order. 您需要做的就是始终按预定义的顺序进行选择。

The moves are left, right and forward. 这些动作是左,右和前进。

If you always make those choices in that order, you'll know what you've already done when you backtrack. 如果你总是按照这个顺序做出这些选择,那么当你回溯时,你就会知道你已经做了什么。

Each step you backtrack, check those moves again. 您回溯的每一步 ,再次检查这些动作。 If you're undoing a right choice, you'll know that you've tried left and right , but haven't yet tried forward . 如果你撤消正确的选择,你会知道你已经试过左右 ,但还没有尝试过前锋

To start off, add all possible moves from the starting position. 首先,从起始位置添加所有可能的移动。 Then, just follow this algorithm: 然后,只需遵循此算法:

At every iteration, try to pop a single frame off the stack. 在每次迭代时,尝试从堆栈中弹出一个帧。 If the stack was empty, you have tried all possible moves. 如果堆栈是空的,那么您已经尝试了所有可能的移动。

Now, look at the position you popped from the stack. 现在,查看从堆栈中弹出的位置。 This is your current position. 这是你目前的职位。

Add all moves from the position you popped which lead to unexplored positions to the stack. 将弹出的位置中的所有移动添加到堆栈中,这些移动将导致未探测的位置。 If any of them is a goal position, you're done. 如果他们中的任何一个是目标位置,那么你就完成了。

The rest will take care of itself. 其余的将照顾自己。 Give it some thought, try a few cases on paper, you'll see. 考虑一下,在纸上尝试一些案例,你会看到。 :) :)

You don't have many solutions: basically, exploring a maze with no loop is like doing a depth first search on the covering tree , each intersection being a node. 你没有很多解决方案:基本上,探索没有循环的迷宫就像在覆盖树上进行深度优先搜索 ,每个交叉点都是一个节点。

You can build the tree as you go, and use that information to walk through it, but this will be tedious an not very efficient . 您可以随时构建树,并使用该信息来遍历它,但这将是乏味的并不是非常有效

A common method of depth first search is to push all the nodes to be checked on the stack, pull one, and push again, until you reach the goal. 深度优先搜索的常用方法是将所有节点推送到堆栈上,拉出一个并再次推送,直到达到目标。 But you get a lot of nodes stacked up, and once you found the target node, you cannot use the stack to know which path you've followed, which means that you need to store that information elsewhere. 但是你会堆积很多节点,一旦找到目标节点,你就无法使用堆栈来知道你所遵循的路径,这意味着你需要将这些信息存储在其他地方。

It is probably better to keep the stack solution and tag the nodes in your stack to indicate a branch, and which direction (ie. which subtree) of the branch have been explored (or which paths are left). 最好保留堆栈解决方案并标记堆栈中的节点以指示分支, 以及 探索分支的哪个方向 (即哪个子树)(或留下哪些路径)。 If you do the exploration always in the same order , that tag can simply be a number: 如果您始终以相同的顺序进行探索,那么该标记可以只是一个数字:

  • 0 for left 左边0
  • 1 for front 1前面
  • 2 for right 2为对
  • 3 for backtrack 3回溯

or better yet an enum. 或更好的枚举。

When a dead end is found, just unwind the stack till you find one of these nodes, and try a new direction. 找到死角后,只需展开堆栈直到找到其中一个节点,然后尝试新的方向。 If all directions have been tried, in other words if there's no direction left, unwind again. 如果已尝试所有方向,换句话说,如果没有方向,请再次展开。

enum Branch {
   LEFT,
   FORWARD,
   RIGHT,
   BACKTRACK
};

struct BacktrackException{
};

template <typename MazeMove>
struct StackNode {
    MazeMove move;
    Branch branch;
    StackNode(MazeMove m): move(m), branch(LEFT) {}
    MazeMove nextBranch(){
        switch(branch){
            case LEFT:
                if (move.can_left()){
                    branch = FORWARD;
                    return move.left();
                }
            case FORWARD:
                if (move.can_forward()){
                    branch = RIGHT;
                    return move.forward();
                }
            case RIGHT:
                if (move.can_right()){
                    branch = BACKTRACK;
                    return move.right();
                }
            default:
                throw BacktrackException();
        }
    }
};

The above code provide a wrapper for a possible "MazeMove" class used with the stack, which keeps track of the attempted direction. 上面的代码为堆栈使用的可能的“MazeMove”类提供了一个包装器,它跟踪尝试的方向。 The nextBranch method returns the next possible move, or throws an exception. nextBranch方法返回下一个可能的移动,或抛出异常。

The advantage is that your stack doesn't get clobbered with untried moves. 优点是你的堆栈不会被未经验证的移动所破坏。 You push a StackNode each time you reach a new position, and unwind it when its options have been all tested out. 每次到达新位置时都会推送StackNode ,并在其选项全部测试完毕后展开它。 When you reach the maze exit, your stack contain only the needed moves. 当您到达迷宫出口时,您的堆栈仅包含所需的移动。

Basically, how do I know how many moves I need to pop off the stack to know I'm back at the original junction, so I can take the other branch instead of the blocked one? 基本上,我怎么知道我需要从堆栈中弹出多少动作才能知道我回到原来的交叉点,所以我可以拿另一个分支而不是阻塞的分支?

You don't – you always go back exactly one step. 你不-你总是回去正好一步 Then check all (remaining) alternatives, then go back one step further … etc. This is called backtracking . 然后检查所有(剩余的)替代方案,然后再往前走......等等。这称为回溯

By the way, this is identical whether you use recursion or not. 顺便说一句,无论你是否使用递归,这都是相同的。 Using recursion just makes the stack implicit, and the stack handling automatic. 使用递归只会使堆栈隐式,并且堆栈处理是自动的。

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

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