java - 递归迷宫求解器无法正常工作

时间:2014-11-30 18:37:58

标签: java algorithm recursion maze

我看过每个帖子都有这个问题,每个指南,每个算法,我仍然无法找到问题的答案。奇怪的是,它并没有引起任何问题。错误,但对于每个迷宫,它说它无法解决,并将开始标记为死胡同。

我盯着我的代码几个小时,我无法弄清楚我做错了什么! 我的代码:

(其他非重要的类 - MazeSymbol =包含符号名称及其字符串的枚举,Position =行/列类,Coordinate =包含MazeSymbol和位置的类))

Maze.java:

private Coordinate[][] map;
private Position start;
private Position goal;
private ArrayList<Coordinate> solutionPath;
boolean solveable;

public Maze(Coordinate[][] map, Position start, Position goal){
    this.start=start;
    this.map=map;
    this.goal=goal;
    this.solutionPath=new ArrayList<>();
    this.solveable = solveMaze(Coordinate.fromMaze(start, this)); //fromMaze takes a position and finds it's symbol.
}
public boolean isSolveable(){
    return solveable;
}
private boolean solveMaze(Coordinate c){
    System.out.println("Solving - "+c.getPosition().row+","+c.getPosition().column);
     if(!c.getPosition().isOnMaze(this)){//Checks if it is between the bounds of the maze (larger than 0 and smaller than length both for X and Y)
         System.out.println(c.getPosition().row+","+c.getPosition().column+"Isnt on maze! ");
         return false;
     }
     if(c.getPosition().equals(goal)){
         System.out.println(c.getPosition().row+","+c.getPosition().column+"Is goal!");
         return true;
     }
     if(!c.getSymbol().equals(MazeSymbol.OPEN)){//If it isnt an open path - walked/dead end/wall
         System.out.println(+c.getPosition().row+","+c.getPosition().column+" isn't open!(it's "+c.getSymbol()+")");
         return false;
     }
     map[c.getPosition().getRow()][c.getPosition().getColumn()] = new Coordinate(c.getPosition(), MazeSymbol.PATH);//Replace the open path sign with a walked path sign
     solutionPath.add(c);//Add the coordinate to the solution path
     if(solveMaze(c.setPosition(c.getPosition().positionAbove())))return true;//row-1                  
     if(solveMaze(c.setPosition(c.getPosition().positionLeft())))return true;//column+1
     if(solveMaze(c.setPosition(c.getPosition().positionBelow())))return true;//row+1
     if(solveMaze(c.setPosition(c.getPosition().positionRight())))return true;//column-1
     map[c.getPosition().getRow()][c.getPosition().getColumn()] = c.setSymbol(MazeSymbol.DEAD_END);//If none was the path, mark it as a dead end
     return false;
    }

请帮忙!当我插入这个非常简单的迷宫时:

{&#34; O&#34;&#34; W&#34;&#34; W&#34;},

{&#34; O&#34;&#34; O&#34;&#34; W&#34;},

{&#34; W&#34;&#34; O&#34;&#34; O&#34;}

它表示无法解决,输出

XWW

OOW

WOO

提前致谢!

编辑:更改了这一行

map[c.getPosition().getRow()][c.getPosition().getColumn()] = new Coordinate(c.getPosition(), MazeSymbol.PATH);

现在它根本不起作用,它只是在2行中产生StackOverflowError然后在第3行中反复出现:

  1. System.out.println("Solving - "+c.getPosition().row+","+c.getPosition().column);
  2. if(solveMaze(c.setPosition(c.getPosition().positionAbove())))return true;
  3. if(solveMaze(c.setPosition(c.getPosition().positionRight())))return true;

2 个答案:

答案 0 :(得分:0)

我在这里猜测,因为您没有给我们Coordinate的确切代码,但假设所有set操作实际上都在Coordinate实例中设置了信息,问题是您在此行中更改了c的符号:

 map[c.getPosition().getRow()][c.getPosition().getColumn()] = c.setSymbol(MazeSymbol.PATH);//Replace the open path sign with a walked path sign

现在,您的c被标记为PATH而不是OPEN。然后你做一个递归调用。让我们说它到了下面的方向,这是它应该看到它是开放的。您设置c的位置,但其值仍为PATH。然后,在递归步骤内,它到达

 if(!c.getSymbol().equals(MazeSymbol.OPEN)){//If it isnt an open path - walked/dead end/wall
     System.out.println(+c.getPosition().row+","+c.getPosition().column+" isn't open!(it's "+c.getSymbol()+")");
     return false;
 }

当然,价值不是OPEN。因此,该位置以及原始位置周围的所有其他位置都失败了。

您需要在新位置更新实际地图中c的值,而不是这样做。

修改

我看了你的问题编辑。你做了什么确实阻止了程序走向死胡同,但实际上并没有解决问题。你看,现在发生的是你更新地图中的位置,但是当你调用递归版本时,坐标保持OPEN的值(或者开头的任何值)。

递归步骤会发生什么?让我们忽略不在地图内的方向。它到了

 if(!c.getSymbol().equals(MazeSymbol.OPEN)){//If it isnt an open path - walked/dead end/wall
     System.out.println(+c.getPosition().row+","+c.getPosition().column+" isn't open!(it's "+c.getSymbol()+")");
     return false;
 }

但无论地图中该坐标的实际内容如何,​​c的值仍为OPEN。所以它不会进入这个if块。它认为它处于开放状态。所以很高兴一次又一次地进入递归步骤。它将转到上面的位置,并且无论地图显示它是P,都会认为它处于打开位置,然后再次递归,返回到下面的位置,依此类推,依此类推。

正如我上面所说,你要做的是从地图的新位置更新c的值。基本上,你永远不会从地图上读书。您永远不会检查地图中的任何位置是墙,路径还是打开。您只需查看cc未从地图更新,因此它不会为您提供所需的信息。

答案 1 :(得分:0)

我建议solveMaze应该使用Position而不是Coordinate,而迷宫可能只是MazeSymbol [] []。

事实上,我根本看不到Coordinate类的用途 - 它只是通过存储另一个MazeSymbol实例来增加混乱,这可能与全局地图不同。我认为这是问题@RealSkeptic强调的建议:

  

从地图的新位置

更新c的值

我只是更进一步,根本不打算使用Coordinate类。

这里的关键概念是当你进行尝试时,你会更新全局状态(迷宫图和解决方案路径);当你从那次尝试中退出时,你可以逆转这些变化。

然后solveMaze的递归部分变为:

private boolean solveMaze(Position p){

    if(!p.isOnMaze(this)) {
        return false;
    }
    if(p.equals(goal)) {
        return true;
    }
    if(!map[p.getRow()][p.getColumn()].equals(MazeSymbol.OPEN)) {   // See NOTE 1
        return false;
    }

    map[p.getRow()][p.getColumn()] = MazeSymbol.PATH;
    solutionPath.add(p);

    if(solveMaze(p.positionAbove())) return true;   // See NOTE 2
    if(solveMaze(p.positionLeft()))  return true;
    if(solveMaze(p.positionBelow())) return true;
    if(solveMaze(p.positionRight())) return true;

    solutionPath.remove(solutionPath.size() - 1);    // See NOTE 3
    map[p.getRow()][p.getColumn()] = MazeSymbol.DEAD_END;

    return false;
}

注1:这将检查实际迷宫中的符号,而不是通过堆栈传递的临时坐标中的符号。

注2:我假设positionAbove / Left ...方法都返回一个新的位置实例,不修改传递的实例;他们可能会这样做,但如果没有代码,我无法确定。

注意3:您需要从解决方案路径中删除无效尝试。

免责声明:没有编译/运行任何此类内容,只需在此输入,因此可能包含拼写错误等。

HTH