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 am working on a dictionary-like program with prolog, and my code goes like this:
define(car,vehicle).
define(car,that).
define(car,has).
define(car,four).
define(car,wheels).
define(wheels,round).
define(wheels,object).
define(wheels,used).
define(wheels,in).
define(wheels,transportation).
defined(X):-define(X,_).
anotherdefined(X):- \+ undefined(X).
undefined(X):- \+define(X,_).
I am trying to write a defined/1 predicate which will give me:
?-defined(X).
X = car ;
X = wheels ;
false.
Yet, my defined/1 gives me X=car. five times (naturally) for everytime it counters define(car,_).
and my anotherdefined/1 gives me only true. What is the method to stop prolog backtracking to the other instances of define(car,_).,and skip to define(wheels,_).?
Edit: I have written the following lines to get the result I want with givedefinedword/1,
listdefined(X):-findall(Y,defined(Y),Z),sort(Z,X).
givedefinedword(X):-listdefined(List),member(X,List).
However since I wanted an efficient predicate (which I will use in many others) it beats the purpose. This predicate does too much process.
Or, Would it be better to use a predicate that modifies the code? say prepares a list of defined words, and modifies it when new definitions are added.
Thanks.
If you change define to relate items and lists, like
definelist(car, [vehicle, that, has, four, wheels]).
% etc.
defined(X) :- definelist(X, _).
then defined will no longer produce duplicates, nor require linear space.
Of course, a query define(X, Y) must now be performed as definelist(X, L), member(Y, L). If you want this to be efficient as well, you may need to duplicate all definitions.
What are you trying to achieve with your program? It seems that you want to have facts in the form:
"A car is a vehicle that has four wheels"
"Wheels are round objects used in transportation" (a bit vague)
How are you going to use these facts? #larsmans suggestion if perfectly fine, if you want to just have your statement as a "sentence". It really depends what you will do with the information though.
Consider structuring the information in your database:
is(car, vehicle).
is(bicycle, vehicle).
is(boat, vehicle).
has(car, wheel(four)).
has(car, motor).
has(bicycle, wheel(two)).
Given this database, you can at least ask a question like, "what vehicles are there?", "does a bicycle have a motor?", or maybe, "how many wheels does a car have?", or "which vehicles have no wheels?"
?- is(X, vehicle).
?- has(bicycle, motor).
?- has(car, wheel(N)).
?- is(X, vehicle), \+ has(X, wheel(_)).
and so on.
Once you have defined your problem better, you can define your data structures better, which will make writing a program to solve your problem easier.
edit: I figured out the singleton variables. Prolog doesn't like captialized words for data banks. I also made some massive changes to the code.
edit:edit: realized I didn't have a recursive call. Derp
I'm pretty new to Prolog though I have some experience in functional programming with Haskell.
Although I am having trouble with trying to make a function output all possible values that make the statement true. I don't think it is a logic error as I have been walking through it for the past few hours but I could be wrong.
In this problem, I am attempting to create the ownership history of a car.
-- I know that a person owns a car IF
-He/She bought the car from the Dealer
-He/She bought it from a previous owner of the car.
So knowing these facts I set up a data bank creating cars, owners and a separate variable of dealer which will function as a base case in this recursion.
car(prius).
car(bmw).
owner(meg).
owner(nora).
dealer(d).
boughtFrom(meg,nora).
boughtFrom(nora,d).
I established that meg bought from nora who bought from the dealer. When you buy from the dealer the recursion I am planning stops as this is the base case end point.
so the logic goes like this:
ownCar(X,Y) :- boughtFrom(X,d), car(Y).
ownCar(X,Y) :- ownCar(boughtFrom(_,prevowner(X)), car(Y)).
You can be the owner of the car if you are a dealer or if you bought the car from a previous owner. and that previous owner is the owner if they bought it from either a dealer or another previous owner. etc etc.
So the only time it stops is when the person bought the car from the dealer.
I think your database is incomplete, because some obvious relation is missing.
This rule, for instance, ownCar(X,Y) :- boughtFrom(X,d), car(Y). will be true for all Y, i.e anybody bought from dealer will 'own' each car.
The representation is also inaccurate. The seller should not more be the owner after she has sold the car.
Anyway, in Prolog you write joins (it's a relational data model), then to get a closure of the transitive property:
ownCar(X, Y) :- owner(O), boughtFrom(X, O), ownCar(O, Y).
Note that in Prolog we should avoid left recursion, because it could lead to infinite loop, then the order of the join is important.
edit
to get the history (as list) you should add an argument to your predicate, or change 'the output' to be a list. You could do something like
ownCar(X, Y, [X]) :- boughtFrom(X,d), car(Y).
ownCar(X, Y, [X|L]) :- owner(O), boughtFrom(X, O), ownCar(O, Y, L).
Note: conventionally in Prolog we place 'output' arguments at last argument position.
So, let's say I have the following in a Prolog database:
person(john).
person(mary).
happy(john).
It is clear to that if I want to list all people, I can type:
person(X).
But, what if I want to find all the things that are true about john? I cannot do:
X(john).
But the effect I would like is to be able to put in "john" and get back "person" and "happy".
There is clearly another way I could store my information:
is(person, john).
is(person, mary).
is(happy, john).
And then, I can do:
is(X, john).
But I lose some expressiveness here. I really would like to be able to do something like:
X(john).
Any ideas?
Thanks!
Parameterizing a query over predicates (as in finding ∀x over x(...)) is not usually possible natively in PROLOG, as this sort of thing is a second- (or, higher)-order logic operation, whereas PROLOG is based on first-order logic.
There are, however, descriptions of how implementations of higher-order logic functions in PROLOG are possible, at least to a limited extent - there are real uses for such functionality. See The Art Of Prolog, Chapter 16, and Higher-order logic programming in Prolog by Lee Naish.
Hm, from my experience, that's not the typical use case of Prolog. If you want to enumerate all "facts" about John, you would first have to define them as terms, and encode their arity. Then you can use call/N and go down the rabbit hole another notch (from memory with the help of GNU Prolog):
relation(1,person).
relation(2,married).
person(john).
married(john,mary).
? relation(1,X), call(X,john).
X = person
| ?- relation(2,X),call(X,john,Y).
X = married
Y = mary
Note that using call has many interesting issues and potential for runtime errors.
This is an approximation:
all_predicates(Term) :-
current_predicate(_, Pred), %% match Pred to any currently defined predicate
\+ predicate_property(Pred, built_in), %% filter out the built-in predicates
functor(Pred, Name, 1), %% check that Pred has 1 argument and match Name to its name
Goal =.. [Name, Term], %% construct the goal Name(Term)
call(Goal). %% Note that if Pred has side effects, they will happen.