[net.lang.prolog] Solution to Who owns the Zebra?

ligett (03/28/83)

	Be warned: this is a long article because it includes a Prolog
program to solve the "Who owns the zebra?/Who drinks the water?"
puzzle I posted a couple of weeks ago.
	Bruce Smith (decvax!duke!unc!bts) was kind enough to mail me
an article entitled "A PROLOG program illustrating a 'verify and choose'
method", by Lewis Baxter, that discussed the solution to this puzzle.
Mr. Baxter wrote the program, and deserves all the credit.  I was slow
in posting this response because I wanted to get Mr. Baxter's permission
to include his program - he has consented.
	The program runs in about 15 seconds under CProlog on our VAX,
and seems to work fine.  I'm embarrassed to say that I don't understand 
all of it yet, but I didn't want to let that stop me from posting the 
solution.
	My thanks to those of you how mailed me hints and help.

Dan Ligett				(617) 649-9731
Wang Institute of Graduate Studies	!decvax!wivax!ligett        (USENET)
70 Tyng Road				ligett.wang-inst@udel-relay (CSNET)
Tyngsboro, MA 01879

/*------------------------------------------------------------------*/
/*  A PROLOG program to solve the "5 houses" problem                */
/*  using a "verify and choose" method.                             */
/*								    */
/*  By Lewis Baxter                                     	    */
/*------------------------------------------------------------------*/

/*  Define ':' as infix.  */		:-op(800,xfx,:).

solve :-
	constraints(Colours,Drinks,Nationalities,Cigarettes,Pets),
	candidate(Colours,Drinks,Nationalities,Cigarettes,Pets),
/****************************************************************/
/*	The next four lines ask	"Who owns the zebra?"		*/
/*	and "Who drinks water?"					*/
/*	member(water:House1,Drinks),				*/
/*	member(Who1:House1,Nationalities), write(Who1), nl,	*/
/*	member(zebra:House2,Pets),				*/
/*	member(Who2:House2,Nationalities), write(Who2), nl.	*/
/****************************************************************/
	write(Colours),       nl,
	write(Drinks),        nl,
	write(Nationalities), nl,
	write(Cigarettes),    nl,
	write(Pets),          nl.

/*  A candidate solution is any of the (5!)**5 ways
    of distributing 5 colours (C), 5 drinks (D),
    5 nationalities (N), 5 cigarettes (S), and 5 pets (P)
    amongst 5 houses.                                               */

candidate([_:C1, _:C2, _:C3, _:C4, _:C5],
          [_:D1, _:D2, _:D3, _:D4, _:D5],
          [_:N1, _:N2, _:N3, _:N4, _:N5],
          [_:S1, _:S2, _:S3, _:S4, _:S5],
          [_:P1, _:P2, _:P3, _:P4, _:P5])  :-
     permutation([C1,C2,C3,C4,C5],[1,2,3,4,5]),
     permutation([D1,D2,D3,D4,D5],[1,2,3,4,5]),
     permutation([N1,N2,N3,N4,N5],[1,2,3,4,5]),
     permutation([S1,S2,S3,S4,S5],[1,2,3,4,5]),
     permutation([P1,P2,P3,P4,P5],[1,2,3,4,5]).

/*  The following constraints are placed on colours (C),
    drinks (D), nationalities (N), cigarettes (S),
    and pets (P).                                                   */

              constraints(C,D,N,S,P)  :-

member(englishman:House1,N),	member(red:House1,C),
member(spaniard:House2,N),	member(dog:House2,P),
member(norwegian:1,N),
member(kools:House3,S),		member(yellow:House3,C),
member(chesterfields:House4,S),	next(House4,Next4),
				member(fox:Next4,P),
member(norwegian:House5,N),	next(House5,Next5),
				member(blue:Next5,C),
member(old_gold:House6,S),	member(snails:House6,P),
member(lucky-strike:House7,S),	member(orange_juice:House7,D),
member(ukranian:House8,N),	member(tea:House8,D),
member(japanese:House9,N),	member(parliaments:House9,S),
member(kools:House10,S),	next(House10,Next10),
				member(horse:Next10,P),
member(coffee:House11,D),	member(green:House11,C),
member(green:House12,C),	left(Left12,House12),
				member(ivory:Left12,C),
member(milk:3,D).


	/*  ---  Utility Predicates  ---  */

permutation([],[]).
permutation([A,..X],Y) :- delete(A,Y,Y1), permutation(X,Y1).

delete(A,[A,..X],X).
delete(A,[B,..X],[B,..Y]) :- delete(A,X,Y).

member(A,[A,..X]) :- !.
member(A1:A2,[B1:B2,..X]) :-
   atomic(A1), atomic(A2), atomic(B1), atomic(B2),
   ( A1 == B1,  A2 \== B2  ;  A2 == B2, A1 \== B1 ),
   !, fail.
member(A,[B,..X]) :- member(A,X).

next(X,Y) :- left(X,Y).    next(X,Y) :- left(Y,X).
left(1,2).  left(2,3).  left(3,4).  left(4,5).

:-end.