Books · Solving Puzzles through Mathematical Programming
Chapter 1
The Fish Puzzle
Five houses stand in a row. Each is painted a different colour. Each is home to a person of a different nationality. Each person owns one distinct pet, smokes one distinct brand of cigar, and drinks one distinct beverage. A list of fifteen clues pins down every attribute of every house except one: the ownership of the fish. The solver is asked to determine who owns the fish.
The Fish puzzle, also known as Einstein’s Riddle and sometimes the Zebra Puzzle, is a classical exercise in constraint satisfaction. It has been attributed (without firm evidence) to Albert Einstein, to Lewis Carroll, and to a Life International puzzle compiler writing in December ; the earliest published form is the version, naming a zebra rather than a fish. The riddle is deceptively simple: the clues are short, the domain is only five houses, and yet the combinatorial structure is tight enough that the solution is unique.
Unlike the grid puzzles of Nikoli, Fish is a pure assignment puzzle: there is no spatial board, only a one-dimensional row of houses indexed through , and five attribute categories each drawn from a size- alphabet. Each of the fifteen clues is a crisp logical predicate on one or two attribute values. The solver’s task is to find the single assignment of attribute values to attribute slots that satisfies every clue.
The CP-SAT encoding is exceptionally clean: each attribute value gets an integer variable in (representing the house index at which it sits), the five attribute categories each carry an constraint, and every clue reduces to either an equality, an order relation, or a Manhattan- adjacency constraint between two such variables.
Rules and a small instance
A Fish puzzle consists of houses in a row, each carrying attributes drawn from non-intersecting domains of size each (in the classical puzzle, ). A solution assigns to each attribute value one of the house indices such that:
Within each attribute category, the values map to the house indices bijectively.
Every clue is satisfied.
In the classical instance the attribute categories are nationality, house colour, pet, cigar brand, and beverage. The fifteen clues are:
The Brit lives in the red house.
The Swede keeps dogs as pets.
The Dane drinks tea.
The green house is immediately to the left of the white house.
The green house owner drinks coffee.
The person who smokes Pall Mall rears birds.
The owner of the yellow house smokes Dunhill.
The person in the centre house drinks milk.
The Norwegian lives in the first house.
The Blends smoker lives next to the cat owner.
The horse owner lives next to the Dunhill smoker.
The Bluemaster smoker drinks beer.
The German smokes Prince.
The Norwegian lives next to the blue house.
The Blends smoker has a neighbour who drinks water.
The unique solution is shown in Figure 1.1.
| H1 | H2 | H3 | H4 | H5 | |
|---|---|---|---|---|---|
| Nationality | Norwegian | Dane | Brit | German | Swede |
| Colour | Yellow | Blue | Red | Green | White |
| Pet | Cat | Horse | Bird | Fish | Dog |
| Cigar | Dunhill | Blends | Pall Mall | Prince | Bluemaster |
| Beverage | Water | Tea | Milk | Coffee | Beer |
The German owns the fish.
The programming model
The tightest encoding gives each attribute value the integer variable representing its house index. With houses and categories of values each, the model has integer variables, each with domain .
Domain bijection per category
For each attribute category (nationality, colour, pet, cigar, beverage), let denote its five values as integer variables. Then enforces that the five values occupy the five distinct houses.
Clue forms
The fifteen clues partition into three syntactic classes.
Identity clues. Most clues assert that two attribute values share a house. For example, clue (“the Brit lives in the red house”) becomes Clues are of this form; so too are clues and (“centre house” and “first house” are specific indices, encoded as and ).
Order clue. Clue says the green house is immediately to the left of the white house. In the integer-variable encoding this is simply
Adjacency clues. Clues are of the form “ lives next to ,” i.e. their house indices differ by exactly one. For clue (“the Blends smoker lives next to the cat owner”): encoded in CP-SAT via on the difference.
A solver in forty lines
from ortools.sat.python import cp_model
NATS = ["Norwegian", "Dane", "Brit", "German", "Swede"]
COLS = ["Yellow", "Blue", "Red", "Green", "White"]
PETS = ["Cat", "Horse", "Bird", "Fish", "Dog"]
CIGS = ["Dunhill", "Blends", "Pallmall", "Prince",
"Bluemaster"]
BEVS = ["Water", "Tea", "Milk", "Coffee", "Beer"]
def solve_fish():
m = cp_model.CpModel()
def attrs(names):
return {n: m.NewIntVar(0, 4, n) for n in names}
nat = attrs(NATS); col = attrs(COLS)
pet = attrs(PETS); cig = attrs(CIGS); bev = attrs(BEVS)
for d in (nat, col, pet, cig, bev):
m.AddAllDifferent(list(d.values()))
# Identity clues
m.Add(nat["Brit"] == col["Red"]) # 1
m.Add(nat["Swede"] == pet["Dog"]) # 2
m.Add(nat["Dane"] == bev["Tea"]) # 3
m.Add(col["Green"] + 1 == col["White"]) # 4
m.Add(col["Green"] == bev["Coffee"]) # 5
m.Add(cig["Pallmall"] == pet["Bird"]) # 6
m.Add(col["Yellow"] == cig["Dunhill"]) # 7
m.Add(bev["Milk"] == 2) # 8
m.Add(nat["Norwegian"] == 0) # 9
m.Add(cig["Bluemaster"] == bev["Beer"]) # 12
m.Add(nat["German"] == cig["Prince"]) # 13
# Adjacency clues (|a - b| == 1)
def adj(a, b):
d = m.NewIntVar(-4, 4, "")
ab = m.NewIntVar(0, 4, "")
m.Add(d == a - b)
m.AddAbsEquality(ab, d)
m.Add(ab == 1)
adj(cig["Blends"], pet["Cat"]) # 10
adj(pet["Horse"], cig["Dunhill"]) # 11
adj(nat["Norwegian"], col["Blue"]) # 14
adj(cig["Blends"], bev["Water"]) # 15
s = cp_model.CpSolver()
s.Solve(m)
# Invert: house index -> attribute values
out = [[""]*5 for _ in range(5)]
for row, d in enumerate((nat, col, pet, cig, bev)):
for name, var in d.items():
out[row][s.Value(var)] = name
return out
The puzzle solves uniquely in about milliseconds. Enumerating all feasible assignments (via ) confirms that precisely one assignment satisfies the fifteen clues.
Sources. The Fish puzzle first appeared in Life International in December in its Zebra form (the unknown pet is a zebra; the magazine framed the puzzle as an attribution to Einstein, though no evidence supports this). The Fish variant circulates widely online and in logic-puzzle anthologies. The fifteen-clue formulation used here is the standard modern version; the uniqueness of solution hinges on the strict “immediately to the left” reading of clue (a relaxed “somewhere to the left” reading admits seven distinct assignments). The fixed puzzle is a tiny finite search, decided at once. Its natural generalisation, in which houses carry attributes and the input is an arbitrary list of equality, order, and adjacency clues, is a constraint-satisfaction problem; deciding whether such a clue system is satisfiable is NP-complete in general, so for the unrestricted family no method essentially faster than search is to be expected.