Prolog (swi for sharing) Predicate to solve a puzzle - prolog

I'm not sure how to even start to write a prolog predicate for a riddle. It gives statements of fact, but nothing more.

To start with, what are some known facts?
fact(north,green,honest).
fact(north,red,lie).
fact(south,green,lie).
fact(south,red,honest).
The question "I'm red or I'm from the South" is possibly a little ambiguous. Is it a boolean algebra OR or an XOR? Should this be "I'm red or I'm from the South, but not both" or should it be "I'm red or I'm from the South, or both"?
Let's tackle "I'm red or I'm from the South, but not both".
Now we can write these two rules pretty easily:
bogg(R,C) :- fact(R,C,honest), R \= south, C = red.
bogg(R,C) :- fact(R,C,honest), R = south, C \= red.
The lie position on this would then be:
bogg(R,C) :- fact(R,C,lie), R = south, C = red.
bogg(R,C) :- fact(R,C,lie), R \= south, C \= red.
If we run that, we get:
?- bogg(R,C).
false.
Clearly, if there is an answer to this problem the statement isn't "I'm red or I'm from the South, but not both".
So let's try "I'm red or I'm from the South, or both":
bogg(R,C) :- fact(R,C,honest), R = south, C = red, !.
bogg(R,C) :- fact(R,C,honest), R = south.
bogg(R,C) :- fact(R,C,honest), C = red.
bogg(R,C) :- fact(R,C,lie), R \= south, C \= red.
Now when I run it I get:
?- bogg(R,C).
R = south,
C = red.

Related

Why is my prolog program stucked in endless recursion

I have the following experimental code
s(a,b).
s(b,c).
s(c,b).
r(a).
r(c).
r(d).
p(X,Y) :- s(X,Y), not(r(Y)).
q(X,Y) :- q(Y,X), r(X).
q(X,Y) :- p(Y,X), s(X,Y).
t(X,Y) :- r(X), q(X,Y).
Querying for t(X,Y) will result in a endless recursion blowing up the stack. But I can actually think of X=c,Y=b being the solution because
t(c,b) <- r(c), q(c,b)
q(c,b) <- q(b,c), r(c)
q(b,c) <- p(c,b), s(b,c)
p(c,b) <- s(c,b), not(r(b))
Can someone explain to me, why prolog doesn't come to this solution and gets caught in an endless recursion around q(c,b) and q(b,c)
Many thanks!
In SWI-Prolog, you can solve the problem using tabled execution.
:- table q/2.
s(a,b).
s(b,c).
s(c,b).
r(a).
r(c).
r(d).
p(X,Y) :- s(X,Y), not(r(Y)).
q(X,Y) :- q(Y,X), r(X).
q(X,Y) :- p(Y,X), s(X,Y).
t(X,Y) :- r(X), q(X,Y).
Examples:
?- t(X,Y).
X = c,
Y = b ;
false.
?- q(X,Y).
X = c,
Y = b ;
X = b,
Y = c.

No implemantation in prolog MAZE

Maze from a to e.
When I run get(a,e[a]).
As I said in the previous comment, the problem is that get/3 is a predicate predefined in library(pce). Fixing your code is simple:
door(a,b).
door(b,c).
door(c,d).
door(d,e).
myget(X,X,A,P) :-
reverse(A,P),
!.
myget(X,Y,A,P) :-
once(door(X,Z);door(Z,X)),
not(member(Z,A)),
format('I am in room ~w.~n', Z),
myget(Z,Y,[Z|A],P).
Query:
?- myget(a,e,[a],P).
I am in room b.
I am in room c.
I am in room d.
I am in room e.
P = [a, b, c, d, e].

Prolog taller than

What is the easiest way to find who is the tallest in Prolog:
height(lisa,1.65).
height(sam,1.70).
height(luke,1.92).
height(nicole,1.54).
I want to write
tallest(X) :- Y is bigger than other Y's
SWI-Prolog has some different ways to solve this problem, for instance by means of library(solution_sequences)
?- order_by([desc(H)],height(P,H)).
H = 1.92,
P = luke ;
...
or using library(aggregate):
?- aggregate(max(H,P),height(P,H),max(_,P)).
P = luke.
less sophisticate Prologs probably will offer setof/3 and last/2:
?- setof(H:P,height(P,H),L),last(L,_:P).
P = luke,
L = [1.54:nicole, 1.65:lisa, 1.7:sam, 1.92:luke].
and still more basic engines, lacking setof/3, will offer
?- height(P,H),\+((height(_,L),L>H)).
P = luke,
H = 1.92 ;
Supposing that tallest(X) succeeds if, and only if, person X is taller than all other persons, I think that a correct answer would be:
tallest(X) :-
height(X, H),
forall((height(Y, H1),
X \= Y),
H > H1), !.
First scenario:
height(lisa,1.65).
height(sam,1.70).
height(luke,1.92).
height(nicole,1.54).
?- tallest(X).
X = luke.
Second scenario:
height(lisa, 1.65).
height(sam, 1.70).
height(luke, 1.92).
height(nicole, 1.54).
height(bob, 1.92). % Bob is as tall as Luke!
?- tallest(X).
false.
height(lisa,1.65).
height(sam,1.70).
height(luke,1.92).
height(nicole,1.54).
max_height(Person, Height, [[Person, Height]]).
max_height(P , H , [[P, H]|Tail]) :- max_height(_ , H2, Tail), H > H2.
max_height(P2, H2, [[_, H]|Tail]) :- max_height(P2, H2, Tail), H =< H2.
tallest(X) :- findall([P, H], height(P, H), Bag), max_height(X, _, Bag).
There are ways to avoid writing max_height : Prolog, find minimum in a list

Why does prolog don't give all results, when I place the equality-comparision at the beginning?

Currently I'm exercising the Prolog chapter of seven languages in seven weeks. I tried to change the coloring example, in order to not write down every valid color combination.
different(red, green).
different(green, red).
different(red, blue).
different(blue, red).
different(blue, green).
different(green, blue).
coloring(A, M, G, T, F) :-
different(M, T),
different(M, A),
different(A, T),
different(A, M),
different(A, G),
different(A, F),
different(G, F),
different(G, T).
I changed the file to make different a functionpredicate:
color(red).
color(blue).
color(green).
different(X, Y) :- color(X), color(Y), \+(X=Y).
different_fail(X, Y) :- \+(X=Y), color(X), color(Y).
coloring(A, M, G, T, F) :-
different(M, T),
different(M, A),
different(A, T),
different(A, M),
different(A, G),
different(A, F),
different(G, F),
different(G, T).
What I don't get is that different gives me all combinations of different colors, but different_fail does only check if they are really different.
GNU Prolog 1.4.5 (32 bits)
Compiled Feb 23 2015, 08:36:31 with gcc
By Daniel Diaz
Copyright (C) 1999-2015 Daniel Diaz
| ?- ['color'].
compiling /home/rudi/7langs/prolog/color.pl for byte code...
/home/rudi/7langs/prolog/color.pl compiled, 17 lines read - 2206 bytes written, 7 ms
| ?- different(red, red).
no
| ?- different(red, blue).
yes
| ?- different_fail(red, red).
no
| ?- different_fail(red, blue).
Up to here everything works as expected.
yes
| ?- different(red, A).
A = blue ? ;
A = green
yes
| ?- different_fail(red, A).
no
But here I would expect that different_fail yields the exact results as different.
What is the difference in these functions, which cause my different_fail to fail?
To declaratively express that two terms be different, use the dif/2 constraint, which is available in SICStus Prolog, SWI-Prolog, YAP and several others.
If you write different_colors/2 like this:
different_colors(X, Y) :- dif(X,Y), color(X), color(Y).
then everything will work exactly as you expect it, no matter in which order you place the goals.
The reason it currently fails is that \+/1 only means not provable at this time. It is not true negation in the declarative sense. Therefore, we have:
?- \+ X = Y, X = a, Y = b.
false.
but if we simply exchange the goals by commutativity of conjunction:
?- X = a, Y = b, \+ X = Y.
X = a,
Y = b.
In contrast, dif/2 works correctly in both cases, and I highly recommend you use it instead:
?- dif(X, Y), X = a, Y = b.
X = a,
Y = b.
?- X = a, Y = b, dif(X, Y).
X = a,
Y = b.
The (=)/2 means unification, not equality, and a variable can unify with anything, by definition.
Then, when you pass an unbound variable to different_fail/2, its first goal will fail, since the unification is actually performed, and then immediately undone by the negation...
I don't agree fully with the answer by mat... I think that in Prolog you cannot avoid to understand the operational semantic...

Don't repeat solutions in Prolog

Suppose you have a database with the following content:
son(a, d).
son(b, d).
son(a, c).
son(b, c).
So a and b are sons of d and c. Now you want to know, given a bigger database, who is brother to who. A solution would be:
brother(X, Y) :-
son(X, P),
son(Y, P),
X \= Y.
The problem with this is that if you ask "brother(X, Y)." and start pressing ";" you'll get redundant results like:
X = a, Y = b;
X = b, Y = a;
X = a, Y = b;
X = b, Y = a;
I can understand why I get these results but I am looking for a way to fix this. What can I do?
Prolog will always try to find every possible solution available for your statements considering your set of truths. The expansion works as depth-first search:
son(a, d).
son(b, d).
son(a, c).
son(b, c).
brother(X, Y) :-
son(X, P),
son(Y, P),
X \= Y.
brother(X, Y)
_______________________|____________________________ [son(X, P)]
| | | |
X = a, P = d X = b, P = d X = a, P = c X = a, P = b
| | | |
| ... ... ...
|
| (X and P are already defined for this branch;
| the algorithm now looks for Y's)
|__________________________________________ [son(Y, d)]
| |
son(a, d) -> Y = a son(b, d) -> Y = b
| |
| | [X \= Y]
X = a, Y = a -> false X = a, Y = b -> true
|
|
solution(X = a, Y = b, P = d)
But, as you can see, the expansion will be performed in all the branches, so you'll end up with more of the same solution as the final answer. As pointed by #Daniel Lyons, you may use the setof built-in.
You may also use the ! -- cut operator -- that stops the "horizontal" expansion, once a branch has been found to be valid, or add some statement that avoids the multiple solutions.
For further information, take a look at the Unification algorithm.
First, I would advise against updating the Prolog database dynamically. For some reasons, consider the article
"How to deal with the Prolog dynamic database?".
You could use a combination of the builtin setof/3 and member/2, as #DanielLyons has suggested in his answer.
As yet another alternative, consider the following query which uses setof/3 in a rather unusual way, like this:
?- setof(t,brother(X,Y),_).
X = a, Y = b ;
X = b, Y = a.
You can eliminate one set with a comparison:
brother(X, Y) :-
son(X, P),
son(Y, P),
X \= Y, X #< Y.
?- brother(X, Y).
X = a,
Y = b ;
X = a,
Y = b ;
false.
Since X and Y will be instantiated both ways, requiring X be less than Y is a good way to cut the solutions in half.
Your second problem is that X and Y are brothers by more than one parent. The easiest solution here would be to make your rules more explicit:
mother(a, d).
mother(b, d).
father(a, c).
father(b, c).
brother(X, Y) :-
mother(X, M), mother(Y, M),
father(X, F), father(Y, F),
X \= Y, X #< Y.
?- brother(X, Y).
X = a,
Y = b ;
false.
This method is very specific to this particular problem, but the underlying reasoning is not: you had two copies because a and b are "brothers" by c and also by d—Prolog was right to produce that solution twice because there was a hidden variable being instantiated to two different values.
A more elegant solution would probably be to use setof/3 to get the solutions. This can work even with your original code:
?- setof(X-Y, (brother(X, Y), X #< Y), Brothers).
Brothers = [a-b].
The downside to this approach is that you wind up with a list rather than Prolog generating different solutions, though you can recover that behavior with member/2.
This should work. But I think it can be improved (I am not a Prolog specialist):
brother(X, Y) :-
son(X, P1),
son(Y, P1),
X #< Y,
(son(X, P2), son(Y, P2), P1 #< P2 -> false; true).
If you're using Strawberry Prolog compiler,you won't get all the answers by typing this:
?- brother(X, Y),
write(X), nl,
write(Y), nl.
In order to get all the answers write this:
?- brother(X, Y),
write(X), nl,
write(Y), nl,
fail.
I hope it helps you.:)
I got to an answer.
% Include the dictionary
:- [p1]. % The dictionary with sons
:- dynamic(found/2).
brother(X, Y) :-
% Get two persons from the database to test
son(X, P),
son(Y, P),
% Test if the two persons are different and were not already used
testBrother(X, Y).
% If it got here it's because there is no one else to test above, so just fail and retract all
brother(_, _) :-
retract(found(_, _)),
fail.
testBrother(X, Y) :-
X \= Y,
\+found(X, Y),
\+found(Y, X),
% If they were not used succed and assert what was found
assert(found(X, Y)).
It always returns fails in the end but it succeeds with the following.
brother(X, Y). % Every brother without repetition
brother('Urraca', X). % Every brother of Urraca without repetition
brother('Urraca', 'Sancho I'). % True, because Urraca and Sancho I have the same father and mother. In fact, even if they only had the same mother or the same father it would return true. A little off context but still valid, if they have three or more common parents it would still work
It fails with the following:
brother(X, X). % False because it's the same person
brother('Nope', X). % False because not is not even in the database
brother('Nope', 'Sancho I'). % False, same reason
So like this I can, for example, ask: brother(X, Y), and start pressing ";" to see every brother and sister without any repetition.
I can also do brother(a, b) and brother(b, a), assuming a and b are persons in the database. This is important because some solutions would use #< to test things and like so brother(b, a) would fail.
So there it is.

Resources