How to check if the paths are connected between rooms - prolog

I have the following facts that build ne of the wings of my dungeon.
path(room1,e,room2).
path(room2,w,room1).
path(room2,e,room5).
path(room5,w,room2).
path(room5,e,room6).
path(room6,w,room5).
path(room2,n,room3).
path(room3,s,room2).
path(room3,e,room4).
path(room4,w,room3).
path(room4,s,room5).
path(room5,n,room4).
path(room5,s,room7).
path(room7,n,room5).
path(room7,e,room8) :- at(key, in_hand).
path(room7,e,room8) :- write('The door appears to be locked.'), nl, fail.
path(room8,w,room7).
path(room2,s,room9).
path(room9,n,room2).
path(room9,s,room10).
path(room10,n,room9).
path(room10,w,room11).
path(room11,e,room10).
path(room11,s,room12).
path(room12,n,room11).
now i want to check if i one room is connected to another and then perhaps display one of the possible paths.
i tried to do something like this
connected(X,Y):-path(X,_,Z),path(Z,_,Y).
but it keeps giving me false, what am i doing wrong.

Try something like
connect(Bgn,End,Path) :- % to find a path between two nodes in the graph
connected(Bgn,End,[],P) , % - we invoke the helper, seeding the visited list as the empty list
reverse(P,Path) % - on success, since the path built has stack semantics, we need to reverse it, unifying it with the result
.
connected(X,Y,V,[X,Y|V]) :- % can we get directly from X to Y?
path(X,_,Y) % - if so, we succeed, marking X and Y as visited
. %
connected(X,Y,V,P) :- % otherwise...
path(X,_,T) , % - if a path exists from X to another room (T)
T \= Y , % - such that T is not Y
\+ member(X,V) , % - and we have not yet visited X
connected(T,Y,[X|V],P) % - we continue on, marking X as visited.
.
You'll note the test for having visited the room before. If your graph has cycles, if you don't have some sort of test for having previously visited the node, you'll wind up going around and around in circles...until you get a stack overflow.

Related

Find all paths between nodes in an undirected graph

I have defined the following network:
connected(d,f).
connected(d,a).
connected(d,b).
connected(d,c).
connected(c,a).
connected(c,b).
connected(c,e).
connected(b,e).
connected(X,Y) :-
edge(X,Y).
connected(X,Y) :-
edge(Y,X).
I need to find all paths between two specified nodes and write them out. I have found a previous post that gives a way to check if there is a path between two nodes but I cannot think of a way how to write out the paths.
path(A,B) :- % two nodes are connected, if
walk(A,B,[]) % - if we can walk from one to the other,
. % first seeding the visited list with the empty list
walk(A,B,V) :- % we can walk from A to B...
edge(A,X) , % - if A is connected to X, and
not(member(X,V)) , % - we haven't yet visited X, and
( % - either
B = X % - X is the desired destination
; % OR
walk(X,B,[A|V]) % - we can get to it from X
) %
. % Easy!

swi-ProLog Finding all possible starting nodes which can arrive specific ending node

I am new to logic programming. I am trying to write a program which can find all nodes which can reach to an specific node.
Here is my code:
link(0, 1).
link(3, 4).
link(1, 2).
link(2, 0).
link(2, 1).
link(3, 2).
link(4, 3).
So show above by graph, it should look like below:
I want to do something like this:
?- go(X,0).
X=1;
X=2;
X=3;
X=4;
....
Which mean that from all 1,2,3 and 4 can go to 0.
[1->2->0],[2->0],[3->2->0],[4->3->2->0]...
so I try
go(X,Y) :- link(X,Y).
go(X,Y) :- link(X,Z),go(Z,Y).
?- go(X,0).
X=2;
X=0;
X=0;
X=0;
X=0;
....
but i don't want 0 as one of the output, it is meaningless to show I can go to 0 when I am already in 0. Also, it keeps repeating.
I try to fix this problem by:
go(X,Y) :- link(X,Y).
go(X,Y) :- (X\==Y),link(X,Z),go(Z,Y).
?- go(X,0).
X = 2 ;
false.
I'm not sure why it will stop at X=2. I try to draw the ProLog search tree and i still don't know why my code wont continue go for other facts(link(3,4)). Seem it stop at some point and no keep going for the green part below:
I try to test it using go(1,0). to go(4,0).
go(1,0) and go(2,0) success
But go(3,0) and go(4,0) return Error: Stack limit. I found that the recursion keep going and going between node 3 and 4. But I don't really know how to solve it.
I am so confused now, please tell me what is my mistake, or how to implement this function in a correct way? Thank you.
The problem is that your graph is a cyclic, rather than acyclic graph. That means that starting from some node X, you [eventually] will return to X.
That means that (for starters) unless you handle the cycles somehow, you will be stuck in an endless recursive loop (until you run out of stack space).
As you traverse the graph, you need to maintain some extra state (a list of nodes that you have already visited). To do this in Prolog, it is a common idiom to use a helper predicate, with the same name but additional arguments that carry the extra state.
So, try something like this:
% the public predicate. We seed the visited list here
% with our starting node.
%
% Not to worry if it is an unbound
% variable. It will become bound/unbound as necessary.
traverse(A,B) :- traverse(A,B,[A]).
traverse(A,B,V) :- % We can get from A to B, if...
link(A,B), % - A and B are directly connected, and
\+ member(B,V) % - we haven't already visited B
. % - easy!
traverse(A,B,V) :- % Otherwise, we can get from A to B, if...
link(A,X), % - we can get to some intermediate node X,
\+ member(X,V) % - that we have not yet visited, and
traverse(X,B,[X|V]) % - we can get from X to B (adding X to the visited list
.

How can I find all the connected cities on graph with Prolog?

Program should check if there is a direct route between two given cities. Alternatively, it can list all the connected cities for a given city.
My Solution is:
I keep a list of cities I visit. If the next city is not the city I came from and the name of the next city is not on the list, I let you press the screen.
My code is:
% knowledge base
path(newyork,losangeles).
path(losangeles,newyork).
path(losangeles,chicago).
path(chicago,losangeles).
path(chicago,houston).
path(houston,chicago).
path(houston,newyork).
path(newyork,houston).
% rules
route(X,Y):-myroute(X,X,Y,[]).
myroute(X,Y,Z,_L):- path(Y,Z), not(X = Z).
myroute(X,Y,A,L):- path(Y,A), not(X = A) , not(member(A,L)),
append(X,L,T) , myroute(Y,A,_Q,T).
Output:
?- route(newyork,Y).
Y = losangeles ;
Y = houston ;
false
Expected Output:
?- route(newyork,Y).
Y = losangeles ;
Y = chicago ;
Y = houston ;
false
My code is check if there is a direct route between two given cities. It can't list all the connected cities for a given city. Where am I making mistakes?
Let's follow the logic of your code:
?- route(newyork, Y).
Prolog tries to unify this with what it knows from the database. It finds this definition:
route(X,Y):-myroute(X,X,Y,[]).
Therefore it unifies X with newyork and proceeds to check if
myroute(newyork, newyork, Y, []) is true, as per the definition of route.
Prolog finds a match for that term in it database, with the first definition of the myroute predicate, and proceeds to unify X, Y and _L in that definition as newyork, newyork and [], i.e. it checks to see if myroute(newyork, newyork, Y, []) can be satisfied given the definition for myroute.
According to the first definition of myroute, prolog checks to see if it can find a fact in its database that will satisfy path(newyork, Z), such that Z is not newyork. There are two facts that can satisfy this, for Z=losangeles and for Z=houston. Note that there is no path(newyork, chicago) fact in the database, therefore this does not qualify as an answer given your current definitions.
Note also that if the first myroute definition fails, then so will the second one, since it checks for exactly the same things first! Therefore there will be no other solutions found via backtracking in this instance.
What you should be doing instead
I won't solve your exercise for you (especially since you didn't ask me to :p ) but in general, your myroute predicate is probably expected to work as follows:
myroute(StartPoint, EndPoint, RouteSoFar) :- % your definition here
and called from route like so:
route(X, Y) :- myroute(X, Y, []).
RouteSoFar is an accumulator; in the first call we call it with [] because we haven't visited any nodes, but in subsequent calls to myroute, it will be a list that you add to before calling the predicate again recursively. Somewhere in your predicate definition you'll have to check that the node you're about to visit isn't in the list of visited nodes already.
The logic of myroute is this: "There exists a myroute from X to Z if there is a simple path from X to Z (base case), OR if there is a path from X to an intermediate UNVISITED node Y, and then a myroute from that Y to Z (recursive case)".

graph recursion in prolog

The graph is shown in the figure below. I have to tell whether there is path between the two nodes and print the path also. For instance: my query is path(a,c). then it will return True. Now when i am giving the query as path(g,f), Ist and 2nd results are True and after that instead of terminating, its starts looping around. I am a novice in prolog. Please suggest some solution for stopping the recursion after getting the correct number of paths
These are the facts
arc(a,b).
arc(b,c).
arc(c,d).
arc(d,b).
arc(b,g).
arc(g,b).
arc(d,g).
arc(g,d).
arc(f,d).
arc(a,e).
arc(f,e).
arc(a,d).
arc(f,g).
arc(c,f).
path(X,Y):- arc(X,Y).
path(X,Y):- arc(X,Z),path(Z,Y).
You are traversing a directed graph. Assuming that it is acyclic, this should enumerate all the possible paths:
path(X,Y) :- % a path exists from X to Y
arc(X,Y) % IF an edge exists *from* X *to* Y
. %
path(X,Y) :- % Otherwise, a path exists from X to Y
arc(X,Z) , % IF an outbound path from X exists
Z \== Y , % whose destination (Z) is NOT Y, and
path(X,Y) % a path exists from Z to Y
. % Easy!
We're using \==/2 here because this predicate might be invoked with unbound argument(s).
Another way to write it might be:
path(X,Y) :- % A path exists from X to Y
arc(X,Z) , % IF an outbound path from X exists,
( % AND
Z = Y % Y is that destination (Z)
; % OR
path(Z,Y) % Y can be reached from Z.
) .
If your graph is cyclic, you'll need to build and carry along with you as you go a list of visited nodes, so you can skip visiting nodes you've already seen, lest you go down the rabbit hole of infinite recursion.

Simplified Travelling Salesman in Prolog

I've looked through the similar questions but can't find anything that's relevant to my problem. I'm struggling to find an algorithm or set of 'loops' that will find a path from CityA to CityB, using a database of
distance(City1,City2,Distance)
facts. What I've managed to do so far is below, but it always backtracks at write(X), and then completes with the final iteration, which is what I want it to do but only to a certain extent.
For example, I don't want it to print out any city names that are dead ends, or to use the final iteration. I want it to basically make a path from CityA to CityB, writing the name of the cities it goes to on the path.
I hope somebody can help me!
all_possible_paths(CityA, CityB) :-
write(CityA),
nl,
loop_process(CityA, CityB).
loop_process(CityA, CityB) :-
CityA == CityB.
loop_process(CityA, CityB) :-
CityA \== CityB,
distance(CityA, X, _),
write(X),
nl,
loop_process(X, CityB).
I tried to demonstrate how you can achieve what you're working on so that you can understand better how it works. So since your OP wasn't very complete, I took some liberties ! Here are the facts I'm working with :
road(birmingham,bristol, 9).
road(london,birmingham, 3).
road(london,bristol, 6).
road(london,plymouth, 5).
road(plymouth,london, 5).
road(portsmouth,london, 4).
road(portsmouth,plymouth, 8).
Here is the predicate we will call to find our paths, get_road/4. It basically calls the working predicate, that has two accumulators (one for the points already visited and one for the distance we went through).
get_road(Start, End, Visited, Result) :-
get_road(Start, End, [Start], 0, Visited, Result).
Here is the working predicate,
get_road/6 : get_road(+Start, +End, +Waypoints, +DistanceAcc, -Visited, -TotalDistance) :
The first clause tells that if there is a road between our first point and our last point, we can end here.
get_road(Start, End, Waypoints, DistanceAcc, Visited, TotalDistance) :-
road(Start, End, Distance),
reverse([End|Waypoints], Visited),
TotalDistance is DistanceAcc + Distance.
The second clause tells that if there is a road between our first point and an intermediate point, we can take it and then solve get_road(intermediate, end).
get_road(Start, End, Waypoints, DistanceAcc, Visited, TotalDistance) :-
road(Start, Waypoint, Distance),
\+ member(Waypoint, Waypoints),
NewDistanceAcc is DistanceAcc + Distance,
get_road(Waypoint, End, [Waypoint|Waypoints], NewDistanceAcc, Visited, TotalDistance).
Usage is as follows :
?- get_road(portsmouth, plymouth, Visited, Distance).
And yields :
Visited = [portsmouth, plymouth],
Distance = 8 ;
Visited = [portsmouth, london, plymouth],
Distance = 9 ;
Visited = [portsmouth, plymouth, london, plymouth],
Distance = 18 ;
false.
I hope it will be helpful to you.
Please separate the pure part from the impure (I/O, like write/1, nl/0 but also (==)/2 and (\==)/2). As long as they are entirely interlaced with your pure code you cannot expect much.
Probably you want a relation between a starting point, an end point and a path in between.
Should that path be acyclic or do you permit cycles?
To ensure that an element X does not occur in a list Xs use the goal maplist(dif(X),Xs).
You do not need any further auxiliary predicates to make this a nice relation!
You should return a successful list as an Out variable in all_possible_paths. Then write out that list. Don't do both in the same procedure.

Resources