迷宫求解器DFS算法

时间:2016-09-03 05:49:26

标签: prolog graph-algorithm maze

我在课程中学习prolog。 我有一个练习,我需要读取文件,从中生成迷宫,获取从源到目的地的路径并将其写入文件。

我读了这个文件,我为每个单元格断言square(CoordX, CoordY) 对于连接的每两个单元格都有,connect(X, Y)

TargetXTargetYSourceXSourceY都是整数坐标,所以我知道起点和终点。

我的工作原理是,如果当前节点连接到目标,则完成并返回。否则,找到当前节点所连接的节点,并使用新节点调用递归

solveFirst(TargetX, TargetY, SourceX, SourceY, T):-
    connects(square(SourceX, SourceY), NewSquare),
    solve(TargetX, TargetY, SourceX, SourceY, NewSquare, T2),
    T = T2.

solve(TargetX, TargetY, SourceX, SourceY, NewSquare ,[Tail]):-
    getFirst(Tail, HeadOfTail),
    (
    connects(NewSquare, square(TargetX, TargetY)), 
    addFirst(NewSquare, Tail, T2),
    Tail = T2 ;
    connects(NewSquare, E),
    solve(TargetX, TargetY, SourceX, SourceY, E, Tail)
    ).

2 个答案:

答案 0 :(得分:2)

您的理由似乎有效。你唯一要做的就是正确实现它。

首先,关于您的代码的一般性评论:

当你看到如下目标时:

T = T2

问问自己:为什么要介绍T2 您只需使用T 代替,因为如果这个目标成功,这些相同的术语

此模式在您的计划中出现两次

另一个问题:从不;放在一行的 end 它看起来与,太相似了。

因此,经过这些更改后,您的solve/6看起来像这样:

solve(TargetX, TargetY, SourceX, SourceY, NewSquare, [Tail]):-
    getFirst(Tail, HeadOfTail),
    (   connects(NewSquare, square(TargetX, TargetY)),
        addFirst(NewSquare, Tail, Tail)
    ;   connects(NewSquare, E),
        solve(TargetX, TargetY, SourceX, SourceY, E, Tail)
    ).

现在问问自己:这是否有意义?特别是,addFirst(NewSquare, Tail, Tail) 成功吗?由于您省略了其定义,因此很难告诉我们。

此外,编译时,您会收到警告

Singleton variables: [HeadOfTail]

那你为什么要引入这个变量?

从概念上讲,以下模式可能会对您有所帮助:

path(Current, Target, Path) :-
    (   connects(Current, Target),
        Path = [Current]
    ;   connects(Current, Next),
        Path = [Current|Ps],
        path(Next, Target, Ps)
    ).

答案 1 :(得分:2)

我已经用这种方式修改了你的规则

solve(TargetX, TargetY, _, _, NewSquare, [NewSquare]) :-
  connects(NewSquare, square(TargetX, TargetY)).

solve(TargetX, TargetY, SourceX, SourceY, NewSquare, [E | Tail]) :-
  connects(NewSquare, E),
  solve(TargetX, TargetY, SourceX, SourceY, E, Tail).

solveFirst(TargetX, TargetY, SourceX, SourceY, [NewSquare | T]):-
  connects(square(SourceX, SourceY), NewSquare),
  solve(TargetX, TargetY, SourceX, SourceY, NewSquare, T).

并且,具有以下事实

square(1, 1).  square(1, 2).  square(1, 3).
square(2, 1).  square(2, 2).  square(2, 3).
square(3, 1).  square(3, 2).  square(3, 3).

connects(square(1, 1), square(1, 2)).
connects(square(1, 1), square(2, 1)).
connects(square(1, 2), square(1, 3)).
connects(square(1, 2), square(2, 2)).
connects(square(2, 1), square(2, 2)).
connects(square(2, 2), square(3, 2)).
connects(square(3, 2), square(3, 3)).

调用solveFirst(3, 3, 1, 1, L),我得到(在L中)以下路径

[square(1,2),square(2,2),square(3,2),square(3,2)]
[square(2,1),square(2,2),square(3,2),square(3,2)]

但这项工作是因为没有循环。如果添加以下连接

connects(square(2, 2), square(1, 2)).

所以你可以循环((1,2) - >(2,2) - >(1,2) - >(2,2)...)和solveFirst(3, 3, 1, 1, L)我得到堆栈溢出。

为避免此问题,您可以记住访问过的方块并避免再次使用它们。

我已经写了以下示例,但考虑到了

(1)我已经切换了开始和目标(先开始,第二个目标)

(2)我在结果路径中添加了开始和目标

(3)我使用gprolog所以我没有not/1;我使用\+ member....代替

getPath(Tx, Ty, Tx, Ty, _, [square(Tx, Ty)]).

getPath(Sx, Sy, Tx, Ty, Visited, [square(Sx, Sy) | Path]) :-
  connects(square(Sx, Sy), square(Nx, Ny)),
  \+ member(square(Nx, Ny), Visited), % or not(member(square(Nx, Ny), Visited)
  getPath(Nx, Ny, Tx, Ty, [square(Nx, Ny) | Visited], Path).

getPath(Sx, Sy, Tx, Ty, Path) :-
  getPath(Sx, Sy, Tx, Ty, [square(Sx, Sy)], Path).

使用以下事实

square(1, 1).  square(1, 2).  square(1, 3).
square(2, 1).  square(2, 2).  square(2, 3).
square(3, 1).  square(3, 2).  square(3, 3).

connects(square(1, 1), square(1, 2)).
connects(square(1, 1), square(2, 1)).
connects(square(1, 2), square(1, 3)).
connects(square(1, 2), square(2, 2)).
connects(square(2, 2), square(1, 2)).
connects(square(2, 1), square(2, 2)).
connects(square(2, 2), square(3, 2)).
connects(square(3, 2), square(3, 3)).

来自getPath(1, 1, 3, 3, L)我得到以下路径

[square(1,1),square(1,2),square(2,2),square(3,2),square(3,3)]
[square(1,1),square(2,1),square(2,2),square(3,2),square(3,3)]

---编辑---

正如Mat在评论中所建议的那样(谢谢!),而不是\+ member(square(Nx, Ny), Visited)(或not(member(square(Nx, Ny), Visited))你可以写(如果你的prolog环境支持maplist/2

maplist(dif(square(Nx,Ny)), Visited)

square(Nx, Ny)列入Visited列表中

这个解决方案更为通用,因为(如果我理解正确的话)统一在两个方向都有效。

相关问题