0

Facts about the situation -- image

(free online OCR:) The police are trying to track down the gang of three kids who have been steeling pumpkins. So far, they have established the following facts: the kids' first names are Angela, Mary, and David; one is 5, one is 7, and one is 8; one has the last name Diamond, and the one with the last name Grant is 3 years older than the one with the Last name Leung. You can assume Angela and Mary are female and David is male.

Use the technique shown in the zebra example discussed in class (the code is available on the course web page) to find missing information on the gang: each child's age, gender, first name and last name, consistent with the data above. Encode the above data as is and do not add additional facts. Document your code appropriately. Additionally, use your Prolog code to show whether or not the computed information uniquely identifies the culprits. Submit these test results and a short explanation of their meaning in the file q2testa.txt.

(Zebra Technique Example.)

I believe the suspects can be organized as follows:

suspect(Angela, _, _, female)
suspect(Mary, _, _, female)
suspect(David, _, _, male)

also I know that the ages are arranged as follows:

suspect( _, Leung, 5, _)
suspect(_, Diamond, 7, _)
suspect(_, Grant,    8, _)

this is where I am stuck as far as how to create the Prolog function to generate a full table of suspects given the above information. Some help would be appreciated.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Hani Salehi
  • 89
  • 1
  • 7
  • 1
    Look for similar problems. Many have been posted here on StackOverflow and solved using Prolog. There is also a common tag here that includes the word `Einstein` in the description of the tag. – Guy Coder Oct 21 '20 at 23:25
  • 2
    The assignment states "use the technique shown in the zebra example discussed in class" . Can You provide that example from class ? There are numerous ways to solve zebra puzzles in Prolog Your solution should match the expected technique . – Kintalken Oct 22 '20 at 00:40
  • I added the zebra example so if you can help me relate this problem to that it would be appreciated – Hani Salehi Oct 22 '20 at 04:36
  • This seems like a rather poor problem for Prolog as it's just a cartesian product of the two sets of clues. Sure Prolog can do it, but there's very little "logic" in it. – Enigmativity Oct 22 '20 at 06:30
  • Since the name of the tag I was hinting at has been named, the tag you need to check is [zebra-puzzle](https://stackoverflow.com/questions/tagged/zebra-puzzle) – Guy Coder Oct 22 '20 at 09:35
  • 1
    More on zebra puzzles here : https://github.com/kintalken/prolog-playground/tree/main/zebra_puzzles – Kintalken Oct 23 '20 at 17:14

3 Answers3

0

:- op(1,'xfy','contains') .

puzzle(CULPRITs)
:-
there_are_three_culprits(CULPRITs) ,
the_set_of_first_names_is_known(CULPRITs) ,
the_set_of_last_names_is_known(CULPRITs) ,
the_set_of_ages_is_known(CULPRITs) ,
one_is_three_years_older(CULPRITs)
.

there_are_three_culprits(CULPRITs)
:-
length(CULPRITs,3)
.

the_set_of_first_names_is_known(CULPRITs)
:-
CULPRITs contains {first_name:'Angela',last_name:_,age:_} ,
CULPRITs contains {first_name:'Mary',last_name:_,age:_} ,
CULPRITs contains {first_name:'David',last_name:_,age:_}
.

the_set_of_last_names_is_known(CULPRITs)
:-
CULPRITs contains {first_name:_,last_name:'Diamond',age:_} ,
CULPRITs contains {first_name:_,last_name:'Grant',age:_} ,
CULPRITs contains {first_name:_,last_name:'Leung',age:_}
.

the_set_of_ages_is_known(CULPRITs)
:-
CULPRITs contains {first_name:_,last_name:_,age:5} ,
CULPRITs contains {first_name:_,last_name:_,age:7} ,
CULPRITs contains {first_name:_,last_name:_,age:8}
.

one_is_three_years_older(CULPRITs)
:-
CULPRITs contains {first_name:_,last_name:'Grant',age:AGE_GRANT} ,
CULPRITs contains {first_name:_,last_name:'Leung',age:AGE_LEUNG} ,
AGE_GRANT is AGE_LEUNG + 3
.

CULPRITs contains CULPRIT
:-
prolog:member(CULPRIT,CULPRITs)
.


There is not an definitive answer to this puzzle . With the given clues there are still 36 possible solutions .

/*
?- puzzle(CULPRITs).

CULPRITs = [{first_name:'Angela', last_name:'Diamond', age:7}, {first_name:'Mary', last_name:'Grant', age:8}, {first_name:'David', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'Angela', last_name:'Diamond', age:7}, {first_name:'Mary', last_name:'Leung', age:5}, {first_name:'David', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'Angela', last_name:'Grant', age:8}, {first_name:'Mary', last_name:'Diamond', age:7}, {first_name:'David', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'Angela', last_name:'Leung', age:5}, {first_name:'Mary', last_name:'Diamond', age:7}, {first_name:'David', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'Angela', last_name:'Grant', age:8}, {first_name:'Mary', last_name:'Leung', age:5}, {first_name:'David', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'Angela', last_name:'Leung', age:5}, {first_name:'Mary', last_name:'Grant', age:8}, {first_name:'David', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'Angela', last_name:'Diamond', age:7}, {first_name:'David', last_name:'Grant', age:8}, {first_name:'Mary', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'Angela', last_name:'Diamond', age:7}, {first_name:'David', last_name:'Leung', age:5}, {first_name:'Mary', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'Angela', last_name:'Grant', age:8}, {first_name:'David', last_name:'Diamond', age:7}, {first_name:'Mary', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'Angela', last_name:'Leung', age:5}, {first_name:'David', last_name:'Diamond', age:7}, {first_name:'Mary', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'Angela', last_name:'Grant', age:8}, {first_name:'David', last_name:'Leung', age:5}, {first_name:'Mary', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'Angela', last_name:'Leung', age:5}, {first_name:'David', last_name:'Grant', age:8}, {first_name:'Mary', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'Mary', last_name:'Diamond', age:7}, {first_name:'Angela', last_name:'Grant', age:8}, {first_name:'David', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'Mary', last_name:'Diamond', age:7}, {first_name:'Angela', last_name:'Leung', age:5}, {first_name:'David', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'Mary', last_name:'Grant', age:8}, {first_name:'Angela', last_name:'Diamond', age:7}, {first_name:'David', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'Mary', last_name:'Leung', age:5}, {first_name:'Angela', last_name:'Diamond', age:7}, {first_name:'David', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'Mary', last_name:'Grant', age:8}, {first_name:'Angela', last_name:'Leung', age:5}, {first_name:'David', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'Mary', last_name:'Leung', age:5}, {first_name:'Angela', last_name:'Grant', age:8}, {first_name:'David', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'David', last_name:'Diamond', age:7}, {first_name:'Angela', last_name:'Grant', age:8}, {first_name:'Mary', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'David', last_name:'Diamond', age:7}, {first_name:'Angela', last_name:'Leung', age:5}, {first_name:'Mary', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'David', last_name:'Grant', age:8}, {first_name:'Angela', last_name:'Diamond', age:7}, {first_name:'Mary', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'David', last_name:'Leung', age:5}, {first_name:'Angela', last_name:'Diamond', age:7}, {first_name:'Mary', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'David', last_name:'Grant', age:8}, {first_name:'Angela', last_name:'Leung', age:5}, {first_name:'Mary', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'David', last_name:'Leung', age:5}, {first_name:'Angela', last_name:'Grant', age:8}, {first_name:'Mary', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'Mary', last_name:'Diamond', age:7}, {first_name:'David', last_name:'Grant', age:8}, {first_name:'Angela', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'Mary', last_name:'Diamond', age:7}, {first_name:'David', last_name:'Leung', age:5}, {first_name:'Angela', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'Mary', last_name:'Grant', age:8}, {first_name:'David', last_name:'Diamond', age:7}, {first_name:'Angela', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'Mary', last_name:'Leung', age:5}, {first_name:'David', last_name:'Diamond', age:7}, {first_name:'Angela', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'Mary', last_name:'Grant', age:8}, {first_name:'David', last_name:'Leung', age:5}, {first_name:'Angela', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'Mary', last_name:'Leung', age:5}, {first_name:'David', last_name:'Grant', age:8}, {first_name:'Angela', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'David', last_name:'Diamond', age:7}, {first_name:'Mary', last_name:'Grant', age:8}, {first_name:'Angela', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'David', last_name:'Diamond', age:7}, {first_name:'Mary', last_name:'Leung', age:5}, {first_name:'Angela', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'David', last_name:'Grant', age:8}, {first_name:'Mary', last_name:'Diamond', age:7}, {first_name:'Angela', last_name:'Leung', age:5}] ;
CULPRITs = [{first_name:'David', last_name:'Leung', age:5}, {first_name:'Mary', last_name:'Diamond', age:7}, {first_name:'Angela', last_name:'Grant', age:8}] ;
CULPRITs = [{first_name:'David', last_name:'Grant', age:8}, {first_name:'Mary', last_name:'Leung', age:5}, {first_name:'Angela', last_name:'Diamond', age:7}] ;
CULPRITs = [{first_name:'David', last_name:'Leung', age:5}, {first_name:'Mary', last_name:'Grant', age:8}, {first_name:'Angela', last_name:'Diamond', age:7}] ;
false .
*/

Kintalken
  • 763
  • 5
  • 9
  • 1
    Note that some of the solutions are just rearrangements of the terms within the list . There are actually 6 unique solutions the other 30 are just rearrangements . – Kintalken Oct 22 '20 at 15:05
0

It's very simple with some basic predicates:

members([],_).
members([M|Ms],Xs) :- select(M,Xs,Ys),members(Ms,Ys).

clue1(Suspects) :- members([[angela,_,_,female],[mary,_,_,female],[david,_,_,male]],Suspects).
clue2(Suspects) :- members([[_,leung,5,_],[_,diamond,7,_],[_,grant,8,_]],Suspects).

solve(Suspects) :-
    Suspects = [[_,_,_,_],[_,_,_,_],[_,_,_,_]],
    clue1(Suspects),
    clue2(Suspects).

That gives me:

?- solve(X).
X = [[angela, leung, 5, female], [mary, diamond, 7, female], [david, grant, 8, male]] ;
X = [[angela, leung, 5, female], [mary, grant, 8, female], [david, diamond, 7, male]] ;
X = [[angela, diamond, 7, female], [mary, leung, 5, female], [david, grant, 8, male]] ;
X = [[angela, grant, 8, female], [mary, leung, 5, female], [david, diamond, 7, male]] ;
X = [[angela, diamond, 7, female], [mary, grant, 8, female], [david, leung, 5, male]] ;
X = [[angela, grant, 8, female], [mary, diamond, 7, female], [david, leung, 5, male]] ;
X = [[angela, leung, 5, female], [david, diamond, 7, male], [mary, grant, 8, female]] ;
X = [[angela, leung, 5, female], [david, grant, 8, male], [mary, diamond, 7, female]] ;
X = [[angela, diamond, 7, female], [david, leung, 5, male], [mary, grant, 8, female]] ;
X = [[angela, grant, 8, female], [david, leung, 5, male], [mary, diamond, 7, female]] ;
X = [[angela, diamond, 7, female], [david, grant, 8, male], [mary, leung, 5, female]] ;
X = [[angela, grant, 8, female], [david, diamond, 7, male], [mary, leung, 5, female]] ;
X = [[mary, leung, 5, female], [angela, diamond, 7, female], [david, grant, 8, male]] ;
X = [[mary, leung, 5, female], [angela, grant, 8, female], [david, diamond, 7, male]] ;
X = [[mary, diamond, 7, female], [angela, leung, 5, female], [david, grant, 8, male]] ;
X = [[mary, grant, 8, female], [angela, leung, 5, female], [david, diamond, 7, male]] ;
X = [[mary, diamond, 7, female], [angela, grant, 8, female], [david, leung, 5, male]] ;
X = [[mary, grant, 8, female], [angela, diamond, 7, female], [david, leung, 5, male]] ;
X = [[david, leung, 5, male], [angela, diamond, 7, female], [mary, grant, 8, female]] ;
X = [[david, leung, 5, male], [angela, grant, 8, female], [mary, diamond, 7, female]] ;
X = [[david, diamond, 7, male], [angela, leung, 5, female], [mary, grant, 8, female]] ;
X = [[david, grant, 8, male], [angela, leung, 5, female], [mary, diamond, 7, female]] ;
X = [[david, diamond, 7, male], [angela, grant, 8, female], [mary, leung, 5, female]] ;
X = [[david, grant, 8, male], [angela, diamond, 7, female], [mary, leung, 5, female]] ;
X = [[mary, leung, 5, female], [david, diamond, 7, male], [angela, grant, 8, female]] ;
X = [[mary, leung, 5, female], [david, grant, 8, male], [angela, diamond, 7, female]] ;
X = [[mary, diamond, 7, female], [david, leung, 5, male], [angela, grant, 8, female]] ;
X = [[mary, grant, 8, female], [david, leung, 5, male], [angela, diamond, 7, female]] ;
X = [[mary, diamond, 7, female], [david, grant, 8, male], [angela, leung, 5, female]] ;
X = [[mary, grant, 8, female], [david, diamond, 7, male], [angela, leung, 5, female]] ;
X = [[david, leung, 5, male], [mary, diamond, 7, female], [angela, grant, 8, female]] ;
X = [[david, leung, 5, male], [mary, grant, 8, female], [angela, diamond, 7, female]] ;
X = [[david, diamond, 7, male], [mary, leung, 5, female], [angela, grant, 8, female]] ;
X = [[david, grant, 8, male], [mary, leung, 5, female], [angela, diamond, 7, female]] ;
X = [[david, diamond, 7, male], [mary, grant, 8, female], [angela, leung, 5, female]] ;
X = [[david, grant, 8, male], [mary, diamond, 7, female], [angela, leung, 5, female]].
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • `members` is not a good *name*, `members(Es, S)` suggests each `E` is a `member(E, S)`. we can just call it [`select`](https://stackoverflow.com/a/8270393/849891), the different arity will keep it apart. more interestingly, we can have the *representation* [derived](https://stackoverflow.com/a/60877178/849891) for us [by Prolog](https://stackoverflow.com/a/20092493/849891) as well. – Will Ness Nov 28 '20 at 11:36
  • Isn't `members` exactly doing what you're saying it does? Every `E` in `Es` is a member of `S`. – Enigmativity Nov 29 '20 at 11:06
  • `members([1,1,1],[1,2,3,4])` is false, isn't it. as it should, here, so it better be named "select", is what I'm saying. from that name alone I'd expect `members([A|B],C):- member(A,C), members(B,C).`. – Will Ness Nov 29 '20 at 13:21
  • @WillNess - Oh, yes, I see what you're saying now. – Enigmativity Nov 29 '20 at 21:47
0

To follow your approach, all you needed to do was to write it down:

find4( Kids) :-
  Kids = [ suspect('Angela', _, _, female),
           suspect('Mary', _, _, female),
           suspect('David', _, _, male) ],
  member( suspect( _, 'Leung', 5, _), Kids),
  member( suspect(_, 'Diamond', 7, _), Kids),
  member( suspect(_, 'Grant',    8, _), Kids).

So you practically already had the solution, although it breaks the conditions specified in the exercise, of "encoding the data as is".

Do note that atoms starting with upper case letters must be enclosed in single quotes, otherwise they will be treated as logical variables.

(previous version of the answer follows).


Advancing slowly by progressive development.

Our first attempts do not have to be working, only the last one does.

known1(P) :-
  P = "the kids' first names are Angela, Mary, and David; 
       one is 5, one is 7, and one is 8; 
       one has the last name Diamond, and 
       the one with the last name Grant is 
          3 years older than the one with the Last name Leung. 
       Angela and Mary are female and David is male".

find1(Q) :-
  Q = "each child's age, gender, first name and last name".

known2(P) :-
  P = [ first_names = [Angela, Mary, David],
        ages =        [5,      7,    8    ],
        last_names =  [Diamond, Grant, X  ],
        ages_of = [[Grant, A1], [Leung, A2]],
        ages_diff = [      A1,          A2,    3],
        males =       [              David],
        females =     [Angela, Mary       ] ].

find2(Q) :-
  known2(P),
  ages(P,Q), genders(P,Q), first_names(P,Q), last_names(P,Q).

Now that we've acquainted ourselves with the situation, we go on defining

find3( Kids ) :-
  Kids = [Angela, Mary, David],  first_name( Angela, angela),
                                 first_name( Mary, mary),
                                 first_name( David, david),
  member( Five,  Kids ),  age( Five, 5),
  member( Seven, Kids ),  age( Seven, 7),
  member( Eight, Kids ),  age( Eight, 8),
  member( Diamond, Kids ),  last_name( Diamond, 'Diamond'),
  member( Grant,   Kids ),  last_name( Grant, 'Grant'),
                            age(       Grant, A1),
  member( Leung,   Kids ),  last_name( Leung, 'Leung'),
                            age(       Leung,      A2),
                            3 is              A1 - A2,
                            female( Angela),
                            female( Mary),
                            male(   David).

So now that we got here, how can we implement those leftover predicates age, male, etc.? By wishful thinking, that's how:

first_name( A, N) :- attr( A, first_name-N).
age(        A, N) :- attr( A, age-N).
last_name(  A, N) :- attr( A, last_name-N).
male(       A   ) :- attr( A, gender-male).
female(     A   ) :- attr( A, gender-female).

And so we've reached the point where we can't delay the actual implementation any longer:

%% (* our workhorse: *)
attr( Rep, Attr-Value) :- 
    memberchk( Attr-X, Rep),       % unique attribute names
    X = Value.

And that's practically it:

17 ?- find3(_Kids), maplist(writeln, _Kids).
[first_name-angela,age-5,last_name-Leung,gender-female|_G5742]
[first_name-mary,age-7,last_name-Diamond,gender-female|_G5751]
[first_name-david,age-8,last_name-Grant,gender-male|_G5760]
true .

(and 5 solutions more).

Will Ness
  • 70,110
  • 9
  • 98
  • 181