1
votes

First off, thank you for dropping by my question. On to the question, I have been working on a Prolog code to model Einstein's riddle. I think it's a pretty elegant solution, it's basically a list of tuples with certain fields such as house color, pet, nationality etc. I have the list modeled and the hints are there as well, but I'm having trouble trying to model the question.

The thing is, I didn't want to stick to just a simple question of "Who owns the fish?". I wanted to model a harder question. For example: "What pet does the person who lives in the house which is to the right of the red house own?". It appears to be a simple check for the index i in the list which has the tuple with the color red, and return the field pet of the tuple at the index i+1 in the list, but my experience with Prolog is pretty limited (it is virtually my first time using it) and I'm not really sure how to implement it. I'll provide the code I have below. I appreciate all answers. Thank you.

persoane(0, []) :- ! .
persoane(N, [(_Men, _Color, _Drink, _Smoke, _Animal)|T]) :- N1 is N-1, persoane(N1,T) .

persoana(1, [H|_], H) :- ! .
persoana(N, [_|T], R) :- N1 is N-1, persoana(N1, T, R)

% Function that tests if A is to the right of B (will be used in the final question)
right(A, B, List) :- nextto(B, A, List).

% The Brit lives in the red house
hint1([(brit, red, _, _, _)|_]) .
hint1([_|T]) :- hint1(T) .

% The Swede keeps dogs as pets
hint2([(swede, _, _, _, dog)|_]) .
hint2([_|T]) :- hint2(T) .

% The Dane drinks tea
hint3([(dane, _, tea, _, _)|_]) .
hint3([_|T]) :- hint3(T) .

% The green house is on the left of the white house
hint4([(_, green, _, _, _,),(_, white, _, _, _)|_]) .
hint4([_|T]) :- hint4(T) .

% The green house owner drinks coffee
hint5([(_, green, cofee, _, _)|_]) .
hint5([_|T]) :- hint5(T) .

% The person who smokes Pall Mall rears birds
hint6([(_, _, _, pallmall, bird)|_]) .
hint6([_|T]) :- hint6(T) .

% The owner of the yellow house smokes Dunhill
hint7([(_, yellow, _, dunhill, _)|_]) .
hint7([_|T]) :- hint7(T) .

% The man living in the center house drinks milk
hint8(persoane) :- persoana(3, persoane, (_, _, milk, _, _)) .

% The Norwegian lives in the first house
hint9(persoane) :- persoana(1, persoane, (norwegian, _, _, _, _)) .

% The man who smokes Blends lives next to the one who keeps cats
hint10([(_, _, _, blend, _),(_, _, _, _, cat)|_]) .
hint10([(_, _, _, _, cat),(_, _, _, blend, _)|_]) .
hint10([_|T]) :- hint10(T) .

% The man who keeps horses lives next to the man who smokes Dunhill
hint11([(_, _, _, dunhill, _),(_, _, _, _, horse)|_]) .
hint11([(_, _, _, _, horse),(_, _, _, dunhill, _)|_]) .
hint11([_|T]) :- hint11(T) .

% The owner who smokes BlueMaster drinks beer
hint12([(_, _, beer, bluemaster, _)|_]) .
hint12([_|T]) :- hint12(T) .

% The German smokes Prince
hint13([(german, _, _, prince, _)|_]) .
hint13([_|T]) :- hint13(T) .

% The Norwegian lives next to the blue house
hint14([(norwegian, _, _, _, _),(_, blue, _, _, _)|_]) .
hint14([(_, blue, _, _, _),(norwegian, _, _, _, _)|_]) .
hint14([_|T]) :- hint14(T) .

% The man who smokes Blend has a neighbour who drinks water
hint15([(_, _, _, blend, _),(_, _, water, _, _)|_]) .
hint15([(_, _, water, _, _),(_, _, _, blend, _)|_]) .
hint15([_|T]) :- hint15(T) .

% The question: What pet does the man who lives to the right of the red house have ?
% question (right((_, _, _, _, _), (_, red, _, _, _), persoane)) .
question([(_, red, _, _, _),()])
question([_|T]) :- question(T) .

solution(persoane) :-
   persoana(5, persoane),
   hint1(persoane),
   hint2(persoane),
   hint3(persoane),
   hint4(persoane),
   hint5(persoane),
   hint6(persoane),
   hint7(persoane),
   hint8(persoane),
   hint9(persoane),
   hint10(persoane),
   hint11(persoane),
   hint12(persoane),
   hint13(persoane),
   hint14(persoane),
   hint15(persoane),
   question(persoane) .

2
What results were you expecting from queries such as persoane(2,X)?Peter Ludemann
(1,2,3,4,5) is not a "tuple". It's short for ','(1,','(2,','(3,','(4,5)))). (Use write_canonical/1 to see this). I think you want to use regular lists, e.g. [1,2,3,4,5]. Your system might have a predicate nth0/3 or nth1/3 --- take a look at how it's implemented.Peter Ludemann
I'm pretty sure persoane(2,X) would just return the information on the "tuple" at index 2, something like (dane, blue, tea, blend, horse) since that's the second house in the solution on Einstein's Riddle. I think it's perfectly fine if no particular information could be extracted from such "tuples", like the pet or the color of the house. I turned to Stackoverflow because I had no ideas left on how to deal with this. I remember checking on things like "Zebra problem" but most of them didn't deal with more advanced questions like the one I mentioned in the quesiton.Hachiko1337
I will look into your suggestions though, perhaps I can find something.Hachiko1337
I managed to do something on this riddle, the code can be found here link. I wonder if there is a way to "query" this data to output the pet of the owner of the house on the right of the red house.Hachiko1337

2 Answers

1
votes

You can access positionally into a list like this:

index(one,   [One, _, _, _, _],   One).
index(two,   [_, Two, _, _, _],   Two).
index(three, [_, _, Three, _, _], Three).
index(four,  [_, _, _, Four, _],  Four).
index(five(, [_, _, _, _, Five],  Five).

You can also define next_to in a similar way:

next_to(List, A, B) :- left_right(List, A, B).
next_to(List, A, B) :- right_left(List, A, B).
right_left(List, A, B) :- left_right(List, B, A).
left_right([A,B,_,_,_], A,B).
left_right([_,A,B,_,_], A,B).
left_right([_,_,A,B,_], A,B).
left_right([_,_,_,A,B], A,B).

There are more generic solutions that work for any length of list (left as an exercise).

1
votes

We can code it top-down style. This means using some made-up predicates and implementing them later, according to how we've just used them:

puzzle( P, Pet ) :- length( P, _ ),
    clue( P, [house-red , nation-brit ] ),    % (* 1. The Brit lives in red house *)
    clue( P, [keeps-dogs, nation-swede] ),    % (* 2. The Swede has dogs as pets  *)
    clue( P, [drinks-tea, nation-dane ] ),                      % (* 3. .....  *)
    clue( P,  left_of, [[house -red   ], [house -white  ]] ),   % (* 4. .....  *)
    clue( P, [house-green,  drinks-coffee  ] ),                 % (*  .......  *)
    clue( P, [keeps-birds,  smokes-pallmall] ),                   
    clue( P, [house-yellow, smokes-dunhill ] ),                   
    clue( P,  center,  [[drinks-milk  ]                  ] ),     
    clue( P,  first,   [[nation-norse ]                  ] ),     
    clue( P,  next_to, [[smokes-blends], [keeps -cats   ]] ),     
    clue( P,  next_to, [[keeps -horses], [smokes-dunhill]] ),     
    clue( P, [smokes-bluemaster, drinks-beer  ] ),                
    clue( P, [nation-german    , smokes-prince] ),
    clue( P,  next_to, [[nation-norse ], [house -blue   ]] ),
    clue( P,  next_to, [[smokes-blends], [drinks-water  ]] ),
    %% (* Who has the zebra ? *)
    clue( P, [keeps-zebra, nation- _Who] ),
    %% (* What pet the man owns who lives to the right of the red house? *)
    clue( P, right_of, [[keeps-Pet   ], [house -red    ]] ),
    maplist( length, P, _ ).

clue( P, AS ) :- member( H, P ), attrs( H, AS ).
clue( P, C, ASs ) :- G =.. [ C, P | ASs], G.

left_of(  P, AS1, AS2 ) :- append( _, [H1, H2 | _], P ),
                                           attrs( H1, AS1 ),
                                           attrs( H2, AS2 ).
next_to(  P, AS1, AS2 ) :- left_of( P, AS1, AS2 ) ; right_of( P, AS1, AS2 ).
right_of( P, AS1, AS2 ) :- left_of( P, AS2, AS1 ).
center(   P,      AS  ) :- middle( P, H ), attrs( H , AS  ).
first(  [H | _],  AS  ) :-                 attrs( H , AS  ).
middle( [H    ], H ).
middle( [_ | R], H ) :- append( X, [_], R ), middle( X, H ).

What's left is to define attrs/2, implementing extensible records of sorts, and using them as a rudimentary object system so that the program "learns" about the properties of the objects involved (here, people) just from their use -- instead of the human programmer infusing their understanding by fixing a specific representation of said objects in the program a priori (as e.g. 5-tuples, etc).

The final goal maplist( length, P, _ ) "freezes" those extensible records (implemented as open-ended lists, for simplicity) by putting the [] into their sentinels (try e.g. X = [1, 2 | _], length( X, _ ) to see what it does).

The goal length( P, _ ) right at the start provides for the iterative deepening approach. Without it the predicate loops, unfortunately.

For nice printout we can run it through

test :- puzzle( P, Pet ), 
        maplist( sort, P, PS ), maplist( writeln, PS ), nl, 
        writeln( Pet ).

producing the output (manually aligned)

62 ?- test, !.
[drinks-water, house-yellow,keeps-cats,  nation-norse, smokes-dunhill   ]
[drinks-tea,   house-blue,  keeps-horses,nation-dane,  smokes-blends    ]
[drinks-milk,  house-red,   keeps-birds, nation-brit,  smokes-pallmall  ]
[drinks-beer,  house-white, keeps-dogs,  nation-swede, smokes-bluemaster]
[drinks-coffee,house-green, keeps-zebra, nation-german,smokes-prince    ]

dogs
true.