Successful prolog query not showing true - prolog

Testing some Prolog code to see if I can show gameover when a white(w) has a connected path from one side of the board to the other.
My biggest problem so far is while gameover/2 does go through all the nodes on each side and checks if their connected it doesn't stop or show when they are. Thus when testing this query:
gameover([[e,e,b,e,e],[e,w,w,b,e],[b,w,b,w,w],[w,w,b,b,b],[e,e,b,w,e]], w).
Prolog returns no when it should be true. I've gone through everything I can but I am new to Prolog and I'm sure I must be missing something obvious.
gameover(State, w) :-
width(State, W), write(W), nl,
series(1, W, NewList), write(NewList), nl,
member(X1, NewList), write(X1), nl,
write(loc(X1,1)), nl, write('X1'), nl,
member(X2, NewList), write(X2), nl,
write(loc(X2,W)), nl, write('X2'), nl,
connected(State, loc(X1,1), loc(X2,W), w).
I've tested connected, member, series and width each very thoroughly and am sure they all work so I think it must be something I have done in the code above.
There is also a similar predicate for gameover(State,b) that uses the Y coordinate rather than the X. It's also having the same troubles.
Connected:
connected(State, Start, End, Colour) :- get_location(State, End, Colour),
get_location(State, Start, Colour),
connects(State, End, Colour, Start, [Start]).
connected(_State, _Next, _Colour, End, Close):- member(End, Close).
%checking to make sure every node is connected to the other using linked
%list of children from linked- check child is not member of closed list(meaning it has been checked before)
%get location of child to check for colour
%recursive call, include children in closed list to mark they have been checked and won't be checked again
connects(State, End, Colour, Next, Close) :- linked(Next, Child),
\+member(Child, Close),
get_location(State, Child, Colour),
connects(State, End, Colour, Child, [Child|Close]).
connects(_State, End, _Colour, End, _Close).

with appropriate simplification, and removing undue display of intermediate computation, here is a sketch that could help you
gameover(State, Colour) :-
width(State, W),
get_location(State, loc(X1,1), Colour),
connected(State, loc(X1,1), loc(_,W), w).
connected(State, L1, L2, Colour) :-
connected_(State, [L1], L2, Colour).
connected_(_State, [Target|_], Target, _Colour).
connected_(State, [Last|Visited], Target, Colour) :-
linked(Last,Link),
get_location(State, Link, Colour),
\+ memberchk(Link, Visited),
connected_(State, [Link,Last|Visited], Target, Colour).
the key it's the service predicate connected_, that holds a list of visited locs instead of the starting one, then enabling a simple depth first visit. A small test, with a silly topology
width(_State, 3).
linked(loc(X,Y),loc(U,V)) :- (U is X+1 ; U is X-1), Y=V.
linked(loc(X,Y),loc(U,V)) :- (V is Y+1 ; V is Y-1), X=U.
get_location(State, loc(X,Y), Colour) :-
nth(Y,State,Row), nth(X,Row,Colour).
?- S = [[e,w,e],[e,w,e],[e,w,e]], gameover(S,w).
S = [[e, w, e], [e, w, e], [e, w, e]]
.
?- S = [[w,e,e],[e,w,e],[e,w,e]], gameover(S,w).
false.

Related

Finding the path length of an Acyclic Graph in Prolog

Okay, so I have the graph:
and I want to be able to create a rule to find all the paths from X to Y and their lengths (number of edges). For
example, another path from a to e exists via d, f, and g. Its length is 4.
So far my code looks like this:
edge(a,b).
edge(b,e).
edge(a,c).
edge(c,d).
edge(e,d).
edge(d,f).
edge(d,g).
path(X, Y):-
edge(X, Y).
path(X, Y):-
edge(X, Z),
path(Z, Y).
I am a bit unsure how I should approach this. I've entered a lot of rules in that don't work and I am now confused. So, I thought I would bring it back to the basics and see what you guys could come up with. I would like to know why you done what you done also if that's possible. Thank you in advance.
This situation has been asked several times already. Firstly, your edge/2 predicates are incomplete, missing edges like edge(c,d), edge(f,g), or edge(g,e).
Secondly, you need to store the list of already visited nodes to avoid creating loops.
Then, when visiting a new node, you must check that this new node is not yet visited, in the Path variable. However, Path is not yet instanciated. So you need a delayed predicate to check looping (all_dif/1). Here is a simplified version using the lazy implementation by https://stackoverflow.com/users/4609915/repeat.
go(X, Y) :-
path(X, Y, Path),
length(Path, N),
write(Path), write(' '), write(N), nl.
path(X, Y, [X, Y]):-
edge(X, Y).
path(X, Y, [X | Path]):-
all_dif(Path),
edge(X, Z),
path(Z, Y, Path).
%https://stackoverflow.com/questions/30328433/definition-of-a-path-trail-walk
%which uses a dynamic predicate for defining path
%Here is the lazy implementation of loop-checking
all_dif(Xs) :- % enforce pairwise term inequality
freeze(Xs, all_dif_aux(Xs,[])). % (may be delayed)
all_dif_aux([], _).
all_dif_aux([E|Es], Vs) :-
maplist(dif(E), Vs), % is never delayed
freeze(Es, all_dif_aux(Es,[E|Vs])). % (may be delayed)
Here are some executions with comments
?- go(a,e).
[a,b,e] 3 %%% three nodes: length=2
true ;
[a,c,d,f,g,e] 6
true ;
[a,c,f,g,e] 5
true ;
[a,d,f,g,e] 5
true ;
false. %%% no more solutions
Is this a reply to your question ?

Prolog - Path finding and length given Relation

I just began learning Prolog and I wanted to understand Pathfinding better. I have a few examples of relationships, however, I don't know how to find the path and length of a relationships when the relationships are cyclical. I've been trying to create a list that documents visited nodes, but I keep receiving errors.
Below are a few examples as well as my attempt to find path given the relationship, source, target, pathlist, and length):
is_a(parallelogram, quadrilateral).
is_a(trapezoid, quadrilateral).
is_a(rhombus, parallelogram).
is_a(rectangle, parallelogram).
is_a(square, rhombus).
is_a(square, rectangle).
edge(a, b).
edge(b, c).
edge(c, d).
edge(d, a).
friend(alice, bob).
friend(bob, carol).
friend(carol, daniel).
friend(carol, eve).
friends(A,B) :-
friend(A,B);
friend(B,A).
transit(Rel, S, T) :-
call(Rel, S, X),
(X = T; transit(Rel, X, T)).
path_(Rel,Source,Target,Path,Len) :-
path_(Rel,Source,Target,Path,Len,[]).
path_(Rel,Source,Target,Path,Len,Visited) :-
transit(Rel,Source,Target),
transit(Rel,Source,Mid),
Mid == Target, !,
append(Visited,[Source,Target],Path),
length(Path,L),
Len is L+1.
path_(Rel,Source,Target,Path,Len,Visited) :-
transit(Rel,Source,Target),
transit(Rel,Source,Mid),
not(member(Mid,Visited)),
path_(Rel,Mid,Target,Path,Len,[Source|Visited]).
The above is my second attempt, but I receive errors on everything. My first attempt only worked with non-cyclical paths, such as for the is_a relationships, which is noted below:
path0(Rel,From,To,[From,To],2) :-
transit(Rel,From,To),
call(Rel, From, To).
path0(Rel,From,To,[From|Xs],Len) :-
transit(Rel,From,X),
call(Rel,From,X),
path0(Rel,X,To,Xs,_),
length(Xs, L),
Len is L+1.

Prolog get list from different relations

i´d like to get a list from a graph. The variables with two letters are the points and the one letter is a line. Each line can contain multiple points.
conn(bs, oc, c).
conn(oc, tc, c).
conn(bs, gp, j).
conn(gp, cc, j).
conn(gp, pc, p).
conn(pc, ls, p).
conn(gp, oc, v).
conn(oc, pc, b).
conn(pc, cc, b).
conn(tc, ls, n).
conn(ls, cc, n).
link(X, Y, Z) :- conn(X, Y, Z), !.
link(X, Y, Z) :- conn(Y, X, Z).
Now i want to get a list with all points belonging to a line. By typing:
getpoints(c, X).
i would expect
X = [bs, oc, tc]
This is how i tried to get my result:
getpoints(Line, [First|[]]) :- not(link(First, _Second, Line)).
getpoints(Line, [First|Rest]) :- link(First, _Second, Line), getpoints(Line, Rest).
Does anybody has an idea?
If you aren't interested in a particular order of points, you could use setof/3
By example
getpoints(Line, Points) :-
setof(X, Y^(conn(X, Y, Line) ; conn(Y, X, Line)), Points).
--- EDIT ---
Is it very complicadet to get the order of the points?
If you're point are really a line (not a more general graph), you can write something like
nextLine(Line, EndLine, []) :-
\+ conn(EndLine, _, Line).
nextLine(Line, Start, [NextPoint | NextLine]) :-
conn(Start, NextPoint, Line),
nextLine(Line, NextPoint, NextLine).
getpoints(Line, [Start, NextPoint | NextLine]) :-
conn(Start, NextPoint, Line),
\+ conn(_, Start, Line),
nextLine(Line, NextPoint, NextLine).
The idea is find, in getpoints/2, the Start point of the line, that is the point that is on the left of a conn/3 (conn(Start, NextPoint, Line)) but isn't on the right of a conn/3 (\+ conn(_, Start, Line)).
In this way you have the first two point of the line and, calling nextLine/3 recursively, you detect the following points in the correct order.

Reversing a list

I need help reversing a list.
fun(a, [b, d]).
fun(b, [c]).
fun(c, []).
fun(d, [e]).
fun(e, [f]).
fun(f, [g]).
fun(g, []).
xyz(X, Y):-
fun(X, Z) -> findall([A|B], (member(A, Z), xyz(A, B)), L),
flatten(L, F), sort(F, Y); Y = [].
The query xyz(a,X). gives me X = [b,c,d,e,f,g].
However, I would like it to give me X = [g,f,e,d,c,b].
I have tried different attempts at reversing the list, but I am not having any luck.
I have tried adding an additional predicate right after this, but it didn't work either:
xyz2(X,Y):-
xyz(X,Y),
reverse(Y,Z),
Z\=0.
Credit goes to CapelliC for the approach to the implementation above found at my other post here.
Recursion in PROLOG?
You can avoid some difficult programming, and make your program easier to read by re-defining your problem. Say the f/2 describes a directed graph, with edges from the first argument to each of the elements in the second argument, so:
a ---> b
a ---> d
b ---> c
% etc
Then, your question is, which nodes in the graph are reachable from a given node? You can define the solution with the help of library(ugraphs).
To make all edges from an f/2:
edge(From-To) :-
f(From, L),
member(To, L).
You can now collect the edges, make a graph, and find which nodes are reachable from a starting node:
foo(X, L) :-
findall(E, edge(E), Edges),
vertices_edges_to_ugraph([], Edges, G),
reachable(X, G, All),
once(select(X, All, R)), % remove the node you start from
reverse(R, L).
Per definition, a node is always reachable from itself, so you need to pick it out of the list of reachable nodes.
?- foo(a, X).
X = [g, f, e, d, c, b].
?- foo(e, X).
X = [g, f].
?- foo(g, X).
X = [].
I don't exactly understand why the order of the elements is significant. This feels a bit like a code smell.

graph path in prolog

I've made the rules to obtain a path of a graph with the edges defined as follows:
graph(a,b).
graph(a,a).
graph(b,c).
but now I need to do it when the facts being, for example:
graph(a,[a,b,e]).
graph(b,[c,d]).
graph(d,[]).
I had this:
path(Origin, Dest, List) :- path(Origin, Dest, List, [Origin]).
path(X, X, List, Temp) :- reverse(Temp, List).
path(Origin, Dest, List, Temp) :- graph(Origin, Inter),
not(member(Inter, Temp)),
path(Inter, Dest, List, [Inter|Temp]).
and I know that problem is in graph(Origin, Inter), but I don't know how to tweak it so it can go inside the list, I've tried graph(Origin, [Inter|_]) but obviously it just checks the first one. Any help (even if it's not code) would be greatly appreciated.
Assuming directed graphs:
edge(X,Y) :- graph(X,Nodes), member(Y,Nodes).
I managed to solve it, in case someone else has the same problem:
node(X) :- graph(X,_).
allnodes(Nodes) :- setof(X, node(X), Nodes).
path(Origin,Dest,List) :- allnodes(X),
path(Origin, Dest, List1, X, X),
reverse(List1,List),!.
path(X,X,[X],_,_).
path(Origin,Dest,[Dest|List],[X|_],AN) :- graph(X, Inter),
member(Dest, Inter),
path(Origin,X,List,AN,AN).
path(Origin,Dest,List,[X|Y],AN) :- graph(X, Inter),
\+ member(Dest, Inter),
path(Origin,Dest,List,Y,AN).
Here's my solution that works on directed or undirected graphs, with or without cycles.
Using #larsmans suggestion you can make this work with adjacency-list like input.
edge(X,Y) :- graph(X,Nodes), member(Y,Nodes).
It also tries to find all paths, without revisiting.
c(1,2).
% ... c(X,Y) means X and Y are connected
d(X,Y):- c(X,Y).
d(X,Y):- c(Y,X).
% Use d instead of c to allow undirected graphs
findPathHelper(_, Begin, [], End):- d(Begin, End).
findPathHelper(Front, Begin, [Next|NMiddle], End):-
not(member(Begin,Front)),
d(Begin, Next),
append([Front,[Begin]], NFront),
findPathHelper(NFront, Next, NMiddle, End).
findPath(Start, End, Path):-
findPathHelper([], Start, Middle, End),
append([[Start],Middle,[End]], Path).

Resources