Some Smullyan style puzzles (Lady and Tiger, Knights and Knaves)

Earlier this year, I ported all the 144 problems from Adrian Groza’s great book “Modelling Puzzles in First Order Logic” ( 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).


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


?- puzzle_37(Ans).
Ans = [lie-tiger, truth-lady] ;
1 Like

@brebs Thanks for your version, Paul.

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…

The same, using clpb:

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


?- puzzle_37(R1Lady, R2Lady).
R1Lady = 0,
R2Lady = 1.
1 Like