如何在Prolog中回溯NxN板?

如何在Prolog中回溯NxN板?,prolog,Prolog,目前,我必须在SWI Prolog中实现Wumpus World,并在NxN大小的板上给出所有可能的路径。我已经完成了几个Prolog教程,但我不知道如何在Prolog中解决这个特定任务。我正试图为我的经纪人找到所有可能的途径,而不是别的。它必须从初始位置(X0,Y0)开始 我附上了迄今为止我成功编写的代码。我曾尝试过做一个简单的DFS,但我很难用变量“parsing”来完成代码 :- dynamic getAllPathsRec/2, agent/2, visited/2, visited/2

目前,我必须在SWI Prolog中实现Wumpus World,并在NxN大小的板上给出所有可能的路径。我已经完成了几个Prolog教程,但我不知道如何在Prolog中解决这个特定任务。我正试图为我的经纪人找到所有可能的途径,而不是别的。它必须从初始位置(X0,Y0)开始

我附上了迄今为止我成功编写的代码。我曾尝试过做一个简单的DFS,但我很难用变量“parsing”来完成代码

:- dynamic getAllPathsRec/2, agent/2, visited/2, visited/2.

gold(5,5).

worldSize(10).

agent(1,1).

getAllPaths :-
    getAllPathsRec(1,1).

getAllPathsRec(X,Y) :-
    format(X), format(Y), format('~n'),
    gold(X1,Y1),
    \+visited(X,Y),
    assert(visited(X,Y)),
    (X = X1, Y = Y1) -> print('Found GOLD');
    move(_,X,Y).

move(right, X, Y) :-
    X1 is X + 1,
    X1 > 0 , X1 < 11,
    getAllPathsRec(X1,Y).
move(left, X, Y) :-
    X1 is X - 1,
    X1 > 0 , X1 < 11,
    getAllPathsRec(X1,Y).
move(up, X, Y) :-
    Y1 is Y + 1,
    Y1 > 0 , Y1 < 11,
    getAllPathsRec(X,Y1).
move(down, X, Y) :-
    Y1 is Y - 1,
    Y1 > 0 , Y1 < 11,
    getAllPathsRec(X,Y1).
:-dynamic getAllPathsRec/2,agent/2,visted/2,visted/2。
黄金(5,5)。
世界规模(10)。
试剂(1,1)。
GetAllPath:-
getAllPathsRec(1,1)。
getAllPathsRec(X,Y):-
格式(X),格式(Y),格式('~n'),
金(X1,Y1),
\+访问(X,Y),
断言(访问(X,Y)),
(X=X1,Y=Y1)->打印(“发现黄金”);
移动(ux,Y)。
移动(右,X,Y):-
X1是X+1,
X1>0,X1<11,
getAllPathsRec(X1,Y)。
移动(左、X、Y):-
X1是X-1,
X1>0,X1<11,
getAllPathsRec(X1,Y)。
移动(向上,X,Y):-
Y1是Y+1,
Y1>0,Y1<11,
getAllPathsRec(X,Y1)。
移动(向下,X,Y):-
Y1是Y-1,
Y1>0,Y1<11,
getAllPathsRec(X,Y1)。
我希望以任何可能的方式找到黄金,理想情况下打印算法所采用的每一条路径。提前谢谢。

编辑:

我注意到,对于足够大的电路板,这种解决方案存在一些效率问题。正在讨论中。当我们得出结果时,我会更新答案


注意
assert/1
predicate,因为它会将事实永久地添加到知识库中,并且在尝试其他组合时不会撤消,因此您将无法访问同一单元格两次

相反,我使用了一个额外的参数
V
(表示已访问),您可以在其中附加在每个探索步骤中处理的元素。此外,我还将每个步骤中选择的方向存储到一个列表
L
,以便在找到目标时打印出来

or运算符
允许在找到目标后不继续探索同一路径,并返回继续尝试其他组合


注意事项:

  • 如果您面临任何可以使用
    assert/1
    的用例,请小心,因为这是一个复杂的问题

  • \uu
    变量在move函数中不需要它,因为您可以简单地添加4个不同的“实现”,只需附加四个方向

  • 作为建议,使用事实或知识(也称世界大小、目标位置和玩家位置)作为变量,不要硬编码。调试和尝试不同的参数会更容易


这里有工作代码和一些输出示例:

:- dynamic 
    getAllPathsRec/2,
    agent/2,
    visited/2.

gold(3, 3).
worldSize(5).
agent(1, 1).

getAllPaths :-
    agent(X, Y),
    getAllPathsRec(X, Y, [], []).

getAllPathsRec(X, Y, V, L) :-
    hashPos(X, Y, H), \+member(H, V), append(V, [H], VP),
    ((gold(X, Y), print(L)) ; move(X, Y, VP, L)).

% Hash H from h(X, Y)
hashPos(X, Y, H) :- H is (X*100 + Y).

% Left
move(X, Y, V, L) :-
    XP is X - 1, XP > 0,
    append(L, [l], LP),
    getAllPathsRec(XP, Y, V, LP).

% Right
move(X, Y, V, L) :-
    XP is X + 1, worldSize(MS), XP =< MS,
    append(L, [r], LP),
    getAllPathsRec(XP, Y, V, LP).

% Up
move(X, Y, V, L) :-
    YP is Y + 1, worldSize(MS), YP =< MS,
    append(L, [u], LP),
    getAllPathsRec(X, YP, V, LP).

% Down
move(X, Y, V, L) :-
    YP is Y - 1, YP > 0,
    append(L, [d], LP),
    getAllPathsRec(X, YP, V, LP).

你在等待一个不使用append/3的答案吗?@GuyCoder为什么不应该使用append/3,它在swipl实现中的std库中…@JosepJoestar它是一个标志,用来查看你的代码是如何工作的,它可能表示效率低下,因为你正在多次遍历相同的数据。不能使用
append/3
,这不是一条黄金法则。当我经常在代码中看到它时,如果我看得足够多,我就会发现效率低下。你的回答很好,我更好奇OP为什么不接受你的回答。哦,酷,我不知道,我会看看执行情况!很高兴知道:)另外,请注意,
散列
是一个快速解决方案,不必处理列表列表,如果映射大小大于3位,它可能无法按预期工作,请随意选择另一种“散列”方式或实现元组列表或列表的成员身份。
?- getAllPaths.
[r,r,r,r,u,l,l,l,l,u,r,r]
true ;
[r,r,r,r,u,l,l,l,l,u,r,u,l,u,r,r,r,r,d,l,l,d]
true ;
[r,r,r,r,u,l,l,l,l,u,r,u,l,u,r,r,r,r,d,l,d,l]
true ;
[r,r,r,r,u,l,l,l,l,u,r,u,l,u,r,r,r,r,d,d,l,l]
true ;
[r,r,r,r,u,l,l,l,l,u,r,u,l,u,r,r,r,r,d,d,l,u,l,d]
true ;
[r,r,r,r,u,l,l,l,l,u,r,u,l,u,r,r,r,d,l,d]
true ;
[r,r,r,r,u,l,l,l,l,u,r,u,l,u,r,r,r,d,r,d,l,l]
true ;
[r,r,r,r,u,l,l,l,l,u,r,u,l,u,r,r,r,d,d,l]
...