Earlier this year, I ported all the 144 problems from Adrian Groza’s great book “Modelling Puzzles in First Order Logic” (https://users.utcluj.ro/~agroza/puzzles/maloga/) to Picat: My Picat page It’s mostly constraint modelling, but some uses plain logic programming). Groza’s solutions are written mostly in Mace4 and some also in Prover9 (Prover9 and Mace4). Especially Mace4 is quite interesting and I’ve added it to my TODO list.
Some of the programs that use plain logic programming has now been ported to SWI-Prolog and might be of some general use. It’s the problems from the Smullyan style puzzles chapters:
(The port from Picat to SWI-Prolog was mostly straightforward. Though in the Lady and Tiger puzzle #48 quite a few things had to be changed since I’ve used Picat’s foreach/1 loop and list comprehensions. But the main approach is the same.)
Interesting. Here’s a different style, using code in variables (is this arguably more Prolog-ish?):
door_opposite(truth, lie).
door_opposite(lie, truth).
contents(lady).
contents(tiger).
says(_, Goal) :-
\+ ground(Goal),
% The \+ rule below relies on ground Goal for soundness
println('Warning: nonground Goal').
says(truth, Goal) :- Goal.
says(lie, Goal) :- \+ Goal.
puzzle_37(Ans) :-
% Truthfulness and Contents
Ans = [T1-C1, T2-C2],
% Setting the scene
contents(C1),
contents(C2),
% 1 sign is truthful, other is lie
door_opposite(T1, T2),
% Door 1: In this room there is a lady and in the other room there is a tiger
says(T1, (C1 = lady, C2 = tiger)),
% Door 2: In one of these rooms there is a lady and in one of these rooms there is a tiger.
says(T2, permutation([C1, C2], [lady, tiger])).
Result:
?- puzzle_37(Ans).
Ans = [lie-tiger, truth-lady] ;
false.
I won’t argue about which of our versions are more Prolog-ish.
My approach is in principle a port of the constraint modelling versions (which I first wrote in Picat) to plain logic programming, e.g. the variable’s domains (::) are represented as member/2, implications/equivalences are replaced with ->/; etc. Also, since different puzzles have different number of special rules I’ve preferred to keep as much as possible contained in each puzzle (except the general says/2). Seen in hindsight some of these rules could surely be generalized neatly and made more Prolog-ish…
:- use_module(library(clpb)).
puzzle_37(Room1Lady, Room2Lady) :-
% In this room there is a lady and in the other room there is a tiger
Sign1 = Room1Lady * ~Room2Lady,
% In one of these rooms there is a lady and in one of these rooms there is a tiger
Sign2 = card([1], [Room1Lady, Room2Lady]),
% Only 1 sign is truthful
sat(card([1], [Sign1, Sign2])).