CIS 554 Notes on Logic Puzzles
Fall 2012, David Matuszek

Well, after telling everyone to avoid negation, I see I have used it in my example. So here are some comments about the program, negation and other stuff.

solve :-
    tie(CrowTie), tie(EvansTie), tie(HurleyTie), tie(SpeiglerTie),
    all_different([CrowTie, EvansTie, HurleyTie, SpeiglerTie]),

Choose some ties. This will choose the same tie for everybody, then repeatedly backtrack, choosing different ties, until the all_different predicate is satisfied. We do the same thing for relatives.

The all_different predicate takes a list and succeeds if the list contains no duplicate values. You can use this predicate without completely understanding it. That's fortunate, because it uses the metalogical ! operator.

    Triples = [ [crow, CrowTie, CrowRelative],
                [evans, EvansTie, EvansRelative],
                [hurley, HurleyTie, HurleyRelative],
                [speigler, SpeiglerTie, SpeiglerRelative] ]
Each list in the triple is [mr, SomeTie, SomeRelative]. Notice that the person owning the tie is specified. We could instead have specfied all the ties, or all the relatives. The important thing is that we cover all three dimensions.
    % 1. The tie with the grinning leprechauns wasn't a present from a daughter.
    \+ member([_, leprechauns, daughter], Triples),

Uh, oh, negation. That's the \+ operator. All this says is that you cannot find, in the list of triples, a triple containing both leprechauns and daughter. The underscore, _, is a variable that could unify with anything, and you don't care what. Later tests are similar.

I said negation was difficult. The above doesn't look difficult, and it isn't. Here's what you need to remember about negation: Whether negation succeeds or fails, it cannot ever unify (instantiate) anything. You can use negation to prevent certain unifications, as above, but you cannot use it to find out anything.