我正在尝试使用回溯来解决使用scala的迷宫。
我的问题是我不断收到StackOverflow错误。
我尝试过很多东西,但我总是以StackOverflow结束。
findStart()和getWay()显示了我使用过的两种方法。
我知道可以通过Streams执行此操作并在一次递归中计算所有可能的方法,但我想首先使用回溯来解决它。
这是我的代码:
object MazeSolver {
case class Cell(free: Boolean, start: Boolean)
type Maze = Seq[Seq[Cell]]
def main(args: Array[String]) = {
val lab = lislab("laby1.rinth")
val entrance = findStart(lab, Position(0, 0))
println(getWay(lab, entrance(0), entrance(0), Nil))
}
def findStart(lab: Maze, pos: Position): List[Position] = {
try {
if (lab(pos.column)(pos.row).start)
List(pos)
else
findStart(lab, pos.south) ::: findStart(lab, pos.north) ::: findStart(lab, pos.west) ::: findStart(lab, pos.east)
} catch {
case _: java.lang.IndexOutOfBoundsException => Nil
}
}
def getWay(maze: Maze, pos: Position, lastPos: Position, way: List[Position]): List[Position] = {
try {
if (!maze(pos.column)(pos.row).free && !maze(pos.column)(pos.row).start) {
Nil
} else {
if (isExit(pos, maze)) {
pos :: way
} else {
val posList = List(pos.north, pos.south, pos.west, pos.east)
posList.foreach { x =>
val tail = getWay(maze, x, pos, way)
if(tail != Nil) tail :: way
}
Nil
}
}
} catch { case _: java.lang.IndexOutOfBoundsException => way }
}
def lislab(fn: String): Maze = {
try {
(for (column <- io.Source.fromFile(fn, "UTF-8").getLines.toList)
yield for (c <- column) yield Cell(c == ' ', c == '?')).toIndexedSeq
} catch { case _: java.io.FileNotFoundException => Nil }
}
case class Position(column: Int, row: Int) {
override def toString = "(" + column + "." + row + ")"
def north = Position(column - 1, row)
def south = Position(column + 1, row)
def west = Position(column, row - 1)
def east = Position(column, row + 1)
}
def isExit(pos: Position, lab: Maze): Boolean = {
val cell = lab(pos.column)(pos.row)
cell.free && (pos.column == 0
|| pos.column == lab.length - 1
|| pos.row == 0
|| pos.row == lab(0).length - 1)
}
}
我做错了什么?如何使findStart()和getWay()函数正常工作?
答案 0 :(得分:1)
我现在只打算对findStart
发表评论。
findStart
有两个问题:
findStart
。不幸的是,任何邻居的相邻小区都是小区本身。 让我们看一个例子,看看为什么第一个点会导致StackOverflow。假设一个迷宫只包含一行三列:
[A] <-> [B] <-> [C (start)]
现在您的算法从A
开始。自A.start == false
起,它调用findStart(A.east)
即findStart(B)
。自B.start == false
起,它就会调用findStart(B.west)
和findStart(B.east)
。第一个与findStart(A)
相同,导致无限递归。
要解决此问题,您需要跟踪已访问过的细胞。实现这一目标的一种方法是传递已访问过的职位列表。
关于第2点,您只需检查maze(position.column)(position.row).free
,检查某个位置是否可以步行。
(未经测试)实施可能如下所示:
def findStart2(maze: Maze, position: Position, visited: List[Position]): List[Position] =
if (maze(position.column)(position.row).start)
List(position) // we reached our goal
else {
val explorationArea: List[Position] = List(position.north, position.east, position.south, position.west) filter (x => !visited.contains(x) && canWalkOnCell(maze, x))
// filter the adjacent positions that have already been visited or are not walkable
explorationArea flatMap {
notYetVisitedPosition =>
findStart2(maze, notYetVisitedPosition, position :: visited) // don't forget to add the current position to the list of already visited positions
}
}
private def canWalkOnCell(maze: Maze, position: Position): Boolean =
indexInBound(maze, position) && maze(position.column)(position.row).free
private def indexInBound(maze: Maze, position: Position): Boolean =
position.column >= 0 && position.row >= 0 && position.column < maze.size && position.row < maze(position.column).size