1

I currently have to make some sort of Wumpus World implementation in SWI Prolog and give all possible paths over a board of size NxN, I have done several prolog tutorials but I can't figure how to solve this particular task in Prolog. I'm trying to get all possible paths for my agent to the gold and nothing else. It has to start from the initial position (X0, Y0).

I attach the code that I've managed to write so far. I have tried to do a simple DFS which sort of works but I struggle with the variable "parsing" to complete the code.

:- 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).

I expect to find the gold in any possible way, ideally printing each path the algorithm has taken. Thank you in advance.

repeat
  • 18,496
  • 4
  • 54
  • 166
  • Are you waiting for an answer that doesn't use append/3 ? – Guy Coder Feb 14 '19 at 19:59
  • 1
    @GuyCoder why shouldn't it use append/3, it's in the std library in swipl implementation... – josepdecid Feb 14 '19 at 20:32
  • 1
    @JosepJoestar It is a flag to look at how your code is working, it can indicate inefficiency because you are traversing the same data more than once. It is not a golden rule that you can not use `append/3`. When I see it in my code often if I look enough I find the inefficiency. Your answer is fine for the question asked, I was more curious as to why the OP did not accept your answer. – Guy Coder Feb 14 '19 at 20:37
  • Oh cool, didn't know that, will take a look to the implementation! Good to know :) – josepdecid Feb 14 '19 at 20:41

1 Answers1

2

EDIT:

I've noticed that this solution has some efficiency problems for boards of enough size. It's being discussed here. I'll update the answer when we come up with a result.


Take care with assert/1 predicate, as it adds the fact to the knowledge base permanently and it's not undone while trying other combinations, so you won't be able to visit the same cell twice.

Instead of that, I approached it with an extra parameter V (that stands for visited), in which you can append the element treated in each exploration step. Also I stored the chosen directions in every step into a list L to print it when the target is found.

The or operator ; allows to not keep exploring the same path once the target is found and goes back to keep trying other combinations.


Notes:

  • If you face any use case where you can use assert/1, take care, because it's deprecated.

  • The _ variable it's not necessary in the move function as you can simply add 4 different "implementations" and just append the four directions.

  • As an advice use the facts or knowledge (a.k.a. World Size, Target position and Player position) as variables and don't hard code it. It'll be easier to debug and try different parameters.


Here you have the working code and some output example:

:- 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).
?- 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]
...
josepdecid
  • 1,737
  • 14
  • 25
  • Also, note that the `hash` is a quick trick-fix to not have to deal with lists of lists, if the map size is greater than 3 digits it could work not as expected, feel free to choose another "hashing" way or implement membership of list of tuples, or list of lists. – josepdecid Feb 14 '19 at 20:31