1

I am new in Prolog. I am trying to develop a Program which goal is, given the X,Y coordinate of a trophy (specified in the fact trofeo(4,3)), and the coordinates of some obstacles (specified in the fact ostacolo(,)), the Program has to find the shorthest path between the initial fixed coordinates(0,0) and the trophy, avoiding obstacles and without returning in visited coordinates.

For each rule you will find a comment explaining it. The main problem is that now, I am trying to generate all the possible paths. The fact is that if I use only the move up and move right actions, (using only the rules X1 is X+1, Y1 is Y and X1 is X, Y1 is Y+1), and without using the rule about the visited position, so without the line: ( \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))), the program gives as output all the possible paths.(For example,in the simple case here, it will return Paths = [[(0, 0), (0, 1), (1, 1)], [(0, 0), (1, 0), (1, 1)]].)

While if I add all the possible actions, and the rule: (\+(visitato(X1,Y1))-> assert(visitato(X1, Y1))), the program find only one of the paths, for example gives as an output only: Paths = [[(0, 0), (1, 0), (0, 0), (0, 1), (1, 1)]]. (it shows the right coordinates, but (0,0) twice, because is not assterted as visited and all in one path).

Thanks in advance to everyone.

% to calculate the max
max([X],X):-!.
max([X|T],X):- max(T,N),X>=N,!.
max([X|T],N):- max(T,N).

:- dynamic visitato/2.

ostacolo(2,0).
trofeo(1,1).

% with this I am finding the maximum X and Y value between obstacles and trophy, to set a limit in the "grid".

max_coordinate(MaxX, MaxY) :-
    findall(X, ostacolo(X, _), XOstacoli),
    findall(Y, ostacolo(_, Y), YOstacoli),
    findall(X, trofeo(X, _), XTesoro),
    findall(Y, trofeo(_, Y), YTesoro),
    append(XTesoro, XOstacoli, ListaX),
    append(YTesoro, YOstacoli, ListaY),
    max(ListaX,MaxX),
    max(ListaY,MaxY).

%every X,Y respects the limit if they are lower or equal to the maximum X and Y calculated before

limite(X, Y) :-
  max_coordinate(MaxX, MaxY),
  X =< MaxX,
  Y =< MaxY.

% base case for recursion
path((X,Y), (X,Y), [(X,Y)], _ ).
% Limit is a constant to not exceed the Stack limit, adiacente is the rule written below
path((X,Y), Trofeo, [(X,Y)|Path], Limit) :- 
  Limit > 0,
  adiacente((X,Y), (X1,Y1)),
  NewLimit is Limit - 1,
  path((X1,Y1), Trofeo, Path,NewLimit).
  
% I recall in the console ?- trofeo(X,Y), find_all_paths((0,0),(X,Y), Paths), to search for the paths

find_all_paths(Start, Trofeo, Paths) :-
   setof(Path, path(Start, Trofeo, Path, 20), Paths).

% basically with this we are saying that there are four possible actions: move up, right, left, down. The coordinates evaluated must be positive(rule positivo), respect the limit(rule limite), must be not an obstacle(rule \+obstacle), and if they are not yet visited(rule \+(visitato(X1,Y1)), then we will insert a fact, that will state that the current X1,Y1 coordinate was visited.

adjacent((X, Y), (X1, Y1)) :-
  (X1 is X, Y1 is Y - 1),
   positivo(X1,Y1),
   limite(X1,Y1),
   \+ostacolo(X1,Y1),
   (   \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))).
adjacent((X, Y), (X1, Y1)) :-
    (X1 is X-1, Y1 is Y),
    positivo(X1,Y1),
    limite(X1,Y1),
   \+ostacolo(X1,Y1),
   (   \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))).
adjacent((X, Y), (X1, Y1)) :- 
    (X1 is X + 1, Y1 is Y),
     positivo(X1,Y1),
     limite(X1,Y1),
     \+ostacolo(X1,Y1),
   (  \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))).
adjacent((X, Y), (X1, Y1)) :-
   (X1 is X, Y1 is Y + 1),
    positivo(X1,Y1),
    limite(X1,Y1),
   \+ostacolo(X1,Y1),
    (  \+(visitato(X1,Y1))-> assert(visitato(X1, Y1))).

% establish that all the coordinates must be positive
positivo(X,Y) :-
    X >= 0, Y >= 0.
false
  • 10,264
  • 13
  • 101
  • 209
MN1005
  • 11
  • 2
  • Show how you query to get your results. In other words, show the top-level interaction that you use. On a separate note, you have a lot of unnecessary parentheses in your code. On yet another note, you shouldn't need to assert anything at all for this problem, I would think? And one more comment, use line breaks. – TA_intern Feb 08 '23 at 12:08
  • @TA_intern I query `?- trofeo(X,Y), find_all_paths((0,0),(X,Y), Paths)`. I assert just to find a way to check if a coordinate was visited or not. I also tried before to do it using lists, but I was not getting the right results. – MN1005 Feb 08 '23 at 13:12
  • Why don't you just assert up front that all obstacle locations have been visited then that should simplify the code. Did not check by running, suspected it by reading only. – Guy Coder Feb 08 '23 at 13:45

1 Answers1

1

The main problem in your code could be the use of assert/1, since it is rather difficult to correctly keep the state syncronized during the search. It's far easier to keep a list of visited (or better, forbidden) coordinates, that gets updated every time you step in your board. So, I suggest to start writing a next_position/3 utility predicate, for instance

next_position(CurrentPosition,ForbiddenPositions,NextPosition) :-
  member(Offset,[(0,1),(1,0),(0,-1),(-1,0)]),
  CurrentPosition=(Cx,Cy),
  Offset=(Ox,Oy),
  NextPosition=(Nx,Ny),
  Nx is Cx+Ox,
  Ny is Cy+Oy,
  \+ memberchk(NextPosition,ForbiddenPositions).

that enumerate (on backtracking) the positions available at every step (note it needs the coordinates of the border of the playground, or it will go to infinity).

Next, you could concentrate on a single path, and only after you got it, start to worry about all paths...

The easier coding for visiting a graph uses the search capability of Prolog. Something like

find_path(Target,Target,_Forbidden,[Target]).
find_path(From,Target,Forbidden,[From|Rest]) :-
  From \= Target,
  next_position(From,Forbidden,Next),
  find_path(Next,Target,[From|Forbidden],Rest).

It's possibile to give a somewhat graphical data rendering for a very simple test (the code above will work only on really small problems...):

test(Path) :-
    Border = [
        (-1,-1),( 0,-1),( 1,-1),( 2,-1),( 3,-1),
        (-1, 0),        /* xx */        ( 3, 0),
        (-1, 1),                        ( 3, 1),
        (-1, 2),/* yy */                ( 3, 2),
        (-1, 3),                /* $$ */( 3, 3),
        (-1, 4),( 0, 4),( 1, 4),( 2, 4),( 3, 4)
    ],
    Start = ( 0, 0),
    Obstacles = [( 1, 0), (0, 2)],
    Trophy = ( 2, 3),
    append(Border,Obstacles,Forbidden),
    find_path(Start,Trophy,Forbidden,Path).
CapelliC
  • 59,646
  • 5
  • 47
  • 90