I want to solve this riddle in prolog:
The students Lily, Jack and Daisy go to the same university. All of them come from a different country and have different hobbies. They all go to a university in the USA, where one of them is living. Lily has better grades then the one who comes from Italy. Jack has better grades then the one who likes reading books. The best grades has the one who likes football. Jack comes from Germany and Daisy likes cooking.
Who is who (name, country, hobby, grades)?
The correct solution should be:
Lily, USA, Reading Books, Grade 2
Jack, Germany, Football, Grade 1
Daisy, Italy, Cooking, Grade 3
The Problem I have right now is that I don't know how I could solve this riddle. How should I define the facts and what's the best way to solve the riddle?
The trick to answer these puzzle questions in Prolog is to generate (retrieve) possible answers and then test them against the logical constraints. So, if Lily is person P1, then retrieve any person P2 and test if that person is from italy. And so forth with the other rules.
That means, in first instance, you need some clauses with possible countries, possible hobbies and possible grades. Not all possibilities are necessary, because some are already ruled out by the question.
The solution below, based on arbitrarily making Lily person 1, Jack person 2 and Daisy person 3.
Load in to Prolog and query who(P1,C1,H1,G1, P2,C2,H2,G2, P3,C3,H3,G3).
country(italy).
country(usa).
hobby(football).
hobby(reading).
grade(c:1).
grade(b:2).
grade(a:3).
who(lily,C1,H1,Grade1, jack,germany,H2,Grade2, daisy,C3,cooking,Grade3):-
country(C1), country(C3), C1 \= C3,
hobby(H1), hobby(H2), H1 \= H2,
grade(G1:Grade1), grade(G2:Grade2), grade(G3:Grade3),
G1 \= G2, G2 \= G3, G1 \= G3,
(C3=italy, G1#>G3),
(H1=reading, G2#>G1),
((H1=football, G1#>G2, G1#>G3); (H2=football, G2#>G1, G2#>G3)).
First, from filling in what we get from the first statement, we have the following.
(Lily, _, _, _)
(Jack,Germany, _, _)
(Daisy, _, Cooking, _)
Where the _ state we don't know something. I should also say that this isn't necessarily prolog, it's more common sense than anything.
We get the phrase "Lily has better grades then the one who comes from Italy", this means that Daisy is from Germany and Lily is from the USA- since Jack's from Germany.
(Lily, USA, _, Grade>Daisy)
(Jack,Germany, _, _)
(Daisy, Italy, Cooking, Grade<Lily)
Next, we have "Jack has better grades then the one who likes reading books", which gives us the fact that he would be the football player, and the next line tells us he has the best grade. We can then promptly fill up the remained and we get:
(Lily, USA, Reading, Grade2)
(Jack,Germany, Football, Grade1)
(Daisy, Italy, Cooking, Grade3)
There could be a program written in prolog that can solve this puzzle in a very roundabout way, but this puzzle is more specific than a general case.
Here is my take. It is essentially what #RdR has, just that I broke up the logic into more predicates, also to have a less overloaded who() main predicate.
name(lily). % (1)
name(jack).
name(daisy).
country(italy).
country(usa).
country(germany).
hobby(football).
hobby(cooking).
hobby(reading).
grade(1).
grade(2).
grade(3).
student(N,C,H,G):- % (2)
name(N), country(C), hobby(H), grade(G).
permute(P,X,Y,Z):- (4)
call(P,X), call(P,Y), call(P,Z) % (6)
, X\=Y, Y\=Z, X\=Z.
students(A,B,C):- (3)
permute(name,N1,N2,N3) % (5)
, permute(country,C1,C2,C3)
, permute(hobby,H1,H2,H3)
, permute(grade,G1,G2,G3)
, A = student(N1,C1,H1,G1) % (7)
, B = student(N2,C2,H2,G2)
, C = student(N3,C3,H3,G3)
.
who(A,B,C):- % (8)
students(A,B,C)
, A = student(lily,C1,H1,G1) % (9)
, B = student(jack,C2,H2,G2)
, C = student(daisy,C3,H3,G3)
, C2 = germany % (10)
, H3 = cooking
, (( C2=italy -> G1 < G2) % (11)
;( C3=italy -> G1 < G3))
, (( H1=reading -> G2 < G1)
;( H3=reading -> G2 < G3))
, (( H1=football -> G1 < G2, G1 < G3)
;( H2=football -> G2 < G1, G2 < G3)
;( H3=football -> G3 < G1, G3 < G2))
.
% Running it:
% ?- who(A,B,C).
% A = student(lily, usa, reading, 2),
% B = student(jack, germany, football, 1),
% C = student(daisy, italy, cooking, 3) ;
% false.
Discussion
So there is quite a bit going on here (which intrigued me), and several choices that could be made differently (hence it is probably a nice contrast to #RdR's solution).
As others pointed out one aspect is how to encode the information that is
given in the problem description. You can express it very concretely (solving
just this case), or be more general (to e.g. allow extending the problem to
more than 3 students).
What makes this problem different from similar others is that you have a mix
of constraints that affect either a singel student ("Jack comes from
Germany"), affect two students ("Lily's grades are better than the one from
Italy"), or involves all of them ("The one liking football has the best
grades").
Moreover, you have disjunction contraints ("They are all from different
contries, and have different hobbies"). Prolog is very good at going through
all the possible instances of a fact, but it is more complicated to have it
pick one instance and leave this one out for the next call of the predicate.
This forces you to find a way to get a set of values from a fact that are
pairwise distinct. (E.g. when Prolog settles for Lily's hobby being reading,
it mustn't also assign reading as Jack's hobby).
So after listing all known facts and their possible values (1) I first
defined a predicate student/4 (2) to simply state that a student has these
4 properties. This produces all possible combination of students and their
attributes, also allowing that they all have the same name, come from the
same country, asf.
It is a good example how to create a result set in Prolog that is way too
large, and then try to narrow it further down (like someone else wrote).
Further predicates could make use of this "generator" and filter more and more
solutions from its result set. This is also easier to test, on each stage you
can check if the intermediate output makes sense.
In a next predicate, students/3 (3), I try exactly what I mentioned
earlier, creating student instances that at least don't use the same
attribute twice (like two students having the same hobby). To achive this I
have to run through all my attribute facts (name/1, country/1, ...), get
three values for each, and make sure they are pairwise distinct.
To not having to explicitly do this for each of the attributes where the
implementation would be always the same except for the name of the
attribute, I constructed a helper predicate permute/4 (4) that I can pass
the attribute name and it will look up the attribute as a fact three times,
and makes sure the bound values are all not the same.
So when I call permute(name,N1,N2,N3) in students/3 (5) it will result in
the lookups call(P,X), call(P,Y), call(P,Z) (6) which results in the same as
invoking name(X), name(Y), name(Z). (As I'm collecting 3 different values
from always 3 facts of the same attribute this is effectively the same as doing the 3-permutations
of a 3-value set, hence the name of helper predicate.)
When I get to (7) I know I have distinct values for each student attribute,
and I just distribute them across three student instances. (This should
actually work the same without the student/4 predicate as you can always
make up structured terms like this on the fly in Prolog. Having the student
predicate offers the additional benefit of checking that no foolish students
can be constructed, like "student(lily, 23, asdf, -7.4)".)
So :- students(A,B,C). produces all possible combinations of 3 students and
their attributes, without using any of the involved attributes twice. Nice.
It also wraps the (more difficult) student() structures in handy
single-letter variables which makes the interface much cleaner.
But aside from those disjointness constraints we haven't implemented any of
the other constraint. These now follow in the (less elegant) who/3
predicate (8). It basically uses students/3 as a generator and tries to
filter out all unwanted solutions by adding more constraints (Hence it has
basically the same signature as students/3.)
Now another interesting part starts, as we have to be able not only to filter
individual student instances, but also to refer to them individually
("Daisy", "Jack", ...) and their respective attributes ("Daisy's hobby"
etc.). So while binding my result variable A, B and C, I pattern-match on
particular names. Hence the literal names lily, jack asf from (9). This
relieves me from considering cases where Lily might come in first, or as
second, or as third (as students/3 would generate such permutations). So
all 3-sets of students that don't come in that order are discarded.
I could just as well have done this later in an explicit constraint like N1 =
lily asf. I do so now enforcing the simple facts that Jack is from Germany
and Daisy likes cooking (10). When those fail Prolog backtracks to the
initial call to students/3, to get another set of students it can try.
Now follow the additional known facts about Lily's grades, Jack's grades, and
the grades of the football lover (11). This is particularly ugly code.
For one, it would be nice to have helper predicate that would be able return
the answer to the query "the student with attribute X". It would take the
current selection of students, A, B and C, an attribute name ('country') and a
value ('italy') and would return the appropriate student. So you could just
query for the student from Italy, rather than assuming it must be the second
OR the third student (as the problem description suggests that Lily herself
is not from Italy).
So this hypothetical helper predicate, let's call it student_from_attribute
would need another helper that finds a value in a student structure by name
and return the corresponding value. Easy in all languages that support some
kind of object/named tuple/record where you can access fields within it by
name. But vanilla Prolog does not. So there would be some lifting to be done,
something that I cannot pull off of the top of my head.
Also the who/3 predicate could take advantage of this other helper, as you
would need a different attribute from the student returned from
student_from_attribute, like 'grade', and compare that to Lily's grade.
That would make all those constraints much nicer, like
student_from_attribute([A,B,C], country, italy, S), attrib_by_name(S, grade,
G), G1 < G. This could be applied the same way to the reading and football
constraint. That would not be shorter, but cleaner and more general.
I'm not sure anybody would read all this :-). Anyway, these considerations made the puzzle interesting for me.
I have several Prolog facts indicating that something or someone is either a person, location or object. I have a clause go(person,location) that indicated that a person moves from where they are to the location given in the clause. However, when I ask the relevant query to find out if someone is at a certain location, Prolog responds with every person that was ever there according to the clauses. How do I go about writing a rule that says that if you are in one location you are by definition not in any of the others?
It appears that you left one important aspect out when modeling the situation as Prolog facts: When did the person go to the location?
Assume you had instead facts of the form:
person_went_to_at(Person, Location, Time).
then it would be pretty easy to determine, for any point in time, where everyone was, and where they moved to last (and, therefore, are now).
You probably need to add timing information to your facts. Imagine the following situtation:
go(dad, kitchen, bathroom).
go(dad, bathroom, garage).
go(dad, garage, kitchen).
Since Prolog is (more or less) declarative, in this case, the actual order of the facts in the file does not matter. So, you cannot conclude that dad is in the kitchen, he might have started from and returned to the garage. Even if you add some kind of starting predicate, say startLoc(dad, kitchen), this does not help with loops (e.g. when you add go(dad, kitchen, outside) to the above rules).
If you add timing information (and leave out the previous room, as this is clear from the timing information), this becomes:
go(dad, bathroom,1).
go(dad, garage,2).
go(dad, kitchen,3).
The actual numbers are not relevant, just their order. You can now get the latest location by ensuring that there is no later "go" command with dad:
location(X, Y) :- go(X, Y, T), \+ ( go(X, _, T2), T2 > T ).
I'm confused about the structure of how my Prolog rules should be written.
Lets say, I want to state that only birds can fly.
Should my rule be written as
fly(X):- bird(X).
or
bird(X):-fly(X).
What is the difference in meaning between them?
Furthermore, do I need to explicitly state that if the entity is not a bird, it cannot fly?
Also, if I wish to say...
I can either go to the zoo or the library. Either zoo or library, but not both. How should it be?
I am assuming it to be..
go_there(X,Place):-
go_there(X,zoo),
go_there(X,library).
Please go gentle on me, as this is my first try with Prolog programming! Thank you!
To say that only birds can fly can be expressed as, if a creature can fly, then it must be a bird. Depending upon the semantics of this terse phrase, it might leave open the possibility that there could be some birds that cannot fly.
Therefore, the predicate:
fly(X) :- bird(X).
Would not be correct. This says that, if X is a bird, then X can fly, which is not what the only birds can fly says, logically.
The more direct translation to Prolog would then be:
bird(X) :- fly(X).
It says that, X is a bird if X can fly (or, if X can fly, then X must be a bird).
The exclusivity of only would occur due to the absence of any other predicate that says some other creature can fly, such as:
bee(X) :- fly(X).
Regarding the second question:
go_there(X,Place):-
go_there(X,zoo),
go_there(X,library).
This says that, X goes to Place if X goes to the zoo, and X goes to the library. This doesn't sound at all like, X can either go to the zoo, or to the library, but not both. I could say something like:
go_there(fred, zoo).
go_there(fred, library).
These say that fred can go to the zoo, or fred can go to the library.
And then if I query:
go_there(fred, Place).
It will yield two different results for Place:
Place = zoo
Place = library
To say that X can go to one or the other, but not both, requires qualification. Does this mean within a specific period of time that X cannot go to both? Or does it mean that once X goes to one, then it can never ever go to the other? Whether the above predicate satisfies the first case is unclear. It depends upon the context. In either case, you'd need to add some Prolog logic to handle either the time aspect, or, if the "forever" case, you'd need a random selection of one or the other, and then have that selection remain persistent from that point forward.
In response to additional comments, if you to say:
go_there(human(fred), zoo).
go_there(human(fred), library).
You are choosing to represent a person, such as fred, as a human via a functor, human/1. In order to designate fred as human, you have to carry the functor around with fred and always refer to fred as human(fred) whenever used in a predicate that is looking for freds membership in the human race. If you wanted to ask if fred goes to the zoo, go_there(fred, zoo). would fail, unfortunately, because the fact that fred is human needs to be a part of the atom representing fred. You'd have to query, go_there(human(fred), zoo). To determine if fred is human, you would query, go_there(human(fred), _). which is also a bit awkward.
Separating the relationships is clearer:
go_there(fred, zoo).
go_there(fred, libary).
human(X) :- go_there(X, zoo).
human(X) :- go_there(X, library).
Now if you query, human(fred)., you get "true". And if you query, go_there(fred, zoo) you also get "true".
I am learning Prolog via http://www.learnprolognow.org and I am having some trouble understanding how to recursively build up a variable with results from another recursive call, as per Practical Session 3.4, question 3. The initial problem is a straight-forward recursive call to determine if a route is feasible. But the follow-on problem asks you to show the actual path to get to the end of the route.
We are given the following knowledge base of travel information:
byCar(auckland,hamilton).
byCar(hamilton,raglan).
byCar(valmont,saarbruecken).
byCar(valmont,metz).
byTrain(metz,frankfurt).
byTrain(saarbruecken,frankfurt).
byTrain(metz,paris).
byTrain(saarbruecken,paris).
byPlane(frankfurt,bangkok).
byPlane(frankfurt,singapore).
byPlane(paris,losAngeles).
byPlane(bangkok,auckland).
byPlane(singapore,auckland).
byPlane(losAngeles,auckland).
Write a predicate travel/2 which determines whether it is possible to
travel from one place to another by chaining together car, train, and
plane journeys. For example, your program should answer yes to the
query travel(valmont,raglan).
I solved this problem with the following code:
travel(From,To) :-
byCar(From,To).
travel(From,To) :-
byTrain(From,To).
travel(From,To) :-
byPlane(From,To).
travel(From,To) :-
byCar(From,NewTo),
travel(NewTo,To).
travel(From,To) :-
byTrain(From,NewTo),
travel(NewTo,To).
travel(From,To) :-
byPlane(From,NewTo),
travel(NewTo,To).
The follow-on problem is:
So, by using travel/2 to query the above database, you can find out
that it is possible to go from Valmont to Raglan. If you are planning
such a voyage, that’s already something useful to know, but you would
probably prefer to have the precise route from Valmont to Raglan.
Write a predicate travel/3 which tells you which route to take when
travelling from one place to another. For example, the program should
respond
X = go(valmont,metz,go(metz,paris,go(paris,losAngeles)))
to the query travel(valmont,losAngeles,X)
I have been struggling to populate X with a series of go(From,To) that show the successive steps of the journey. It looks like a recursive problem but I do not know how one should go about tackling it. This technique seems fundamental to Prolog programming, and I am quite interested in the thinking process to solve this problem and I look forward to any insight you can provide.
I had a go at this. I made one change to your first solution, just to remove some redundancy. I used the predicate connected/2 to generalize the relationship common to all connections appearing in the by_car/2, by_train/2, and by_plane/2 facts:
connected(From, To) :- by_car(From, To).
connected(From, To) :- by_train(From, To).
connected(From, To) :- by_plane(From, To).
Then I defined travel/2 as a recursive relationship over connected/2:
travel(From, To) :-
connected(From, To).
travel(From, To) :-
connected(From, Through),
travel(Through, To).
Turning to travel/3, notice that the final connection in the nested go... terms is a structure go/2, but the rest are go/3s. So we need to populate X with a series of nested go/3 structures that terminate in a go/2. This last is our base condition. Then it is simply a matter of repeating the second clause of travel/2, but including a go/3 in the third argument that will capture the values instantiated to From and Through on each iteration:
travel(From, To, go(From, To)) :-
connected(From, To).
travel(From, To, go(From, Through, Route)) :-
connected(From, Through),
travel(Through, To, Route).