Logic Puzzles With Prolog
Logic Puzzles With Prolog
A vast array of interesting and commonly known logic puzzles can be elegantly and efficiently
solved with Prolog and constraints. Some puzzles can be very directly modeled and solved as
combinatorial tasks, others need more effort to find a suitable formulation as such tasks, and yet
other puzzles require a search over different states. In the following, we consider a few
example puzzles.
Video:
Premise: You are on an island where every inhabitant is either a knight or a knave. Knights
always tell the truth, and knaves always lie.
Using Prolog and its CLP(B) constraints, we can model this situation as follows:
https://www.metalevel.at/prolog/puzzles 1/7
28/08/2020 Logic Puzzles with Prolog
use one Boolean variable for each inhabitant, its truth value representing whether the
person is a knave or a knight.
A single query over Boolean variables suffices to determine the kind of each inhabitant, by
expressing the relation between A and the truth value of the statement:
?- sat(A =:= ~A+B).
A = B, B = 1.
Using CLP(B) constraints, we can use the Boolean expression card(Ls,Exprs) to express a
cardinality constraint: This expression is true iff the number of expressions in Exprs that evaluate
to true is a member of the list Ls of integers. Thus, for this example, we can use:
?- sat(A =:= card([1,2],[~A,~B])).
A = 1,
B = 0.
Or, equivalently:
?- sat(A =:= ~A + ~B).
A = 1,
B = 0.
Example 4: You meet 3 inhabitants. A says: "All of us are knaves." B says: "Exactly one of us
is a knight."
Example 5: A says: "B is a knave." B says: "A and C are of the same kind." What is C?
This shows that C is definitely a knave. A and B can both be either knights or knaves, and they
are of different kinds, which is indicated by the residual goal.
Again, we can trivially translate this puzzle to statements over propositional logic. For example,
let us use the Boolean variables A1, A2, ..., A6 to denote whether or not the corresponding answer
is selected. Then the different statements can be translated to relations over Boolean formulas
as follows:
solution([A1,A2,A3,A4,A5,A6]) :-
sat(A1 =:= A2*A3*A4*A5*A6),
sat(A2 =:= ~(A3+A4+A5+A6)),
sat(A3 =:= A1*A2),
sat(A4 =:= A1+A2+A3),
sat(A5 =:= ~(A1+A2+A3+A4)),
sat(A6 =:= ~(A1+A2+A3+A4+A5)).
In this formulation, we are again assuming that your Prolog system ships with a dedicated
constraint solver over Boolean domains that implements sat/1 as it is used above. For example,
in SICStus Prolog and Scryer Prolog, such a solver is available as library(clpb).
The following interaction shows that only a single answer (option 5) can be selected in such a
way that all statements are consistent.
?- solution(Vs).
Vs = [0, 0, 0, 0, 1, 0].
The Prolog system has automatically deduced the single admissible answer via
constraint propagation.
https://www.metalevel.at/prolog/puzzles 3/7
28/08/2020 Logic Puzzles with Prolog
Lewis Carroll
Lewis Carroll was a logic teacher who also published many entertaining puzzles.
In many of these puzzles, your job is to string together all given statements so that they form a
chain of implications, typically arriving at a result that is surprising or amusing. For example:
Once more, we can translate each of these statements to a formula of propositional logic, but this
time it is not so straight-forward because the language is intentionally a bit obfuscated. We make
the simplifying assumption that all entities mentioned are met with at sea, since this qualification
occurs, albeit cryptically, in each of the statements. Let us use the following abbreviations:
N it is noticed
M it is a mermaid
L it is entered in the log
R it is worth remembering
I I have seen it
It only remains to find a chain of implications that links all statements or their negations:
implication_chain([], Prev) --> [Prev].
implication_chain(Vs0, Prev) --> [Prev],
{ select(V, Vs0, Vs) },
( { taut(Prev =< V, 1) } -> implication_chain(Vs, V)
; { taut(Prev =< ~V, 1) } -> implication_chain(Vs, ~V)
).
Sample query:
?- sea(Vs),
Vs = [N,M,L,R,I],
select(Start, Vs, Rest),
phrase(implication_chain(Rest, Start), Cs).
In this case, the two solutions for Cs are: [M,N,L,R,~I] and [I,~R,~L,~N,~M]. Informally, this
translates to "If it is a mermaid, I have not seen it", and "If I have seen it, it is not a mermaid",
respectively. The whole chain, in the first case, is: If it is a mermaid, it is noticed, hence it is
https://www.metalevel.at/prolog/puzzles 4/7
28/08/2020 Logic Puzzles with Prolog
entered in the log, hence it is worth remembering, hence I have not seen it. Informally, the
"solution" of this puzzle is: I have never seen a mermaid.
Such deductions also frequently arise or are needed when applying Prolog for theorem proving.
Cryptoarithmetic puzzles
Cryptoarithmetic puzzles require us to find digits corresponding to letters or symbols, such that
the represented numbers satisfy certain constraints.
Integer constraints are a great fit for such puzzles. Let us first define the relation between a list of
digits and the represented number:
digits_number(Ds, N) :-
length(Ds, _),
Ds ins 0..9,
reverse(Ds, RDs),
foldl(pow, RDs, 0-0, N-_).
The first solution is: 12+83+579=674. On backtracking, all solutions are generated. We can easily
express further constraints. For example, we can generate solutions where T is not 0 by adding the
constraint T#\=0. This yields the solution 23+74+968=1065.
Zebra Puzzle
There is a well-known puzzle commonly known as Zebra Puzzle.
https://www.metalevel.at/prolog/puzzles 5/7
28/08/2020 Logic Puzzles with Prolog
Zebra Puzzle: There are five houses, each painted in a unique color. Their inhabitants are from
different nations, own different pets, drink different beverages and smoke different brands of
cigarettes.
Such puzzles can be very conveniently solved by first translating the entities to integers, and then
using your Prolog system's declarative integer arithmetic to state the given hints as relations
between variables whose domains are sets of integers. For example, if the position of a house is
represented as a variable H with domain {1,2,3,4,5}, then the following relation holds for any
neighbouring house N:
abs(H-N) #= 1
This relation works correctly in all directions, no matter which of the two variables, if any, is
already instantiated. Thus, translating such puzzles to integers often increases convenience when
expressing the desired relations, and often also improves performance due to pruning techniques
that are automatically applied. In addition, keeping your programs pure lets you benefit from
powerful additional techniques such as declarative debugging.
next_to(H, N) :- abs(H-N) #= 1.
Dozens of questions on Stackoverflow are about solving this famous puzzle or closely related
ones with Prolog. You can thus read the existing questions and answers for more information.
Main page
https://www.metalevel.at/prolog/puzzles 7/7