1

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) . 
7
  • What results were you expecting from queries such as persoane(2,X)? Commented Mar 23, 2020 at 12:46
  • (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. Commented Mar 23, 2020 at 12:50
  • 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. Commented Mar 23, 2020 at 13:37
  • I will look into your suggestions though, perhaps I can find something. Commented Mar 23, 2020 at 13:38
  • 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. Commented Mar 23, 2020 at 17:55

2 Answers 2

1

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).

Sign up to request clarification or add additional context in comments.

Comments

1

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.):

attr( House, Attr-Value) :- % progressively instantiated memberchk( Attr-X, House), % unique attribute names X = Value. attrs( H, AS) :- maplist( attr(H),AS ). 

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 ?- time( (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 % 167,950 inferences, 0.047 CPU in 0.050 seconds (94% CPU, 3588652 Lips) true. 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.