Thursday, February 17, 2011

Einstein's Riddle as CSP

This python script solves a riddle known on the web as "Einstein's riddle", although there seems to be little if any support for Einstein actually creating it. Nevertheless, it has become my "hello world" for machine learning/reasoning technologies.
The riddle as described here is as follows:
Einsteins' Riddle
  1. In a street there are five houses, painted five different colours.
  2. In each house lives a person of different nationality
  3. These five homeowners each drink a different kind of beverage, smoke a different brand of cigar and keep a different pet.
THE QUESTION: WHO OWNS THE FISH?

HINTS
  1. The Brit lives in a red house.
  2. The Swede keeps dogs as pets.
  3. The Dane drinks tea.
  4. The Green house is next to, and on the left of the White house.
  5. The owner of the Green house drinks coffee.
  6. The person who smokes Pall Mall rears birds.
  7. The owner of the Yellow house smokes Dunhill.
  8. The man living in the centre house drinks milk.
  9. The Norwegian lives in the first house.
  10. The man who smokes Blends lives next to the one who keeps cats.
  11. The man who keeps horses lives next to the man who smokes Dunhill.
  12. The man who smokes Blue Master drinks beer.
  13. The German smokes Prince.
  14. The Norwegian lives next to the blue house.
  15. The man who smokes Blends has a neighbour who drinks water.
Here is a simple python script using python-constraint:

from constraint import *
problem
= Problem()
def left_of(a,b):
return a < b
def right_of(a,b):
return not left_of(a,b)
def next_to(a,b):
return abs(a - b) == 1
colors
= ["yellow","red","green","blue","white"]
nats
= ["norwegian","dane","brit","german","swede"]
bevs
= ["water","tea","milk","coffee","beer"]
smokes
= ["dunhill","blends","pall_mall", "prince", "bluemaster"]
pets
= ["cat","horse","bird","fish","dog"]

house_numbers
= [1,2,3,4,5]

problem
.addVariables(colors, house_numbers)
problem
.addVariables(nats, house_numbers)
problem
.addVariables(bevs, house_numbers)
problem
.addVariables(smokes, house_numbers)
problem
.addVariables(pets, house_numbers)

problem
.addConstraint(AllDifferentConstraint(),colors)
problem
.addConstraint(AllDifferentConstraint(),nats)
problem
.addConstraint(AllDifferentConstraint(),bevs)
problem
.addConstraint(AllDifferentConstraint(),smokes)
problem
.addConstraint(AllDifferentConstraint(),pets)

#specific constraints from the riddle
problem
.addConstraint(lambda brit, red: brit == red, ("brit", "red"))
problem
.addConstraint(lambda swede, dog: swede == dog, ("swede", "dog"))
problem
.addConstraint(lambda dane, tea: dane == tea, ("dane", "tea"))
problem
.addConstraint(lambda green, white: next_to(green, white) and \
left_of(green, white),(
"green", "white"))
problem
.addConstraint(lambda green, coffee: green == coffee,
(
"green", "coffee"))
problem
.addConstraint(lambda pall_mall, bird: pall_mall == bird,
(
"pall_mall", "bird"))
problem
.addConstraint(lambda yellow, dunhill: yellow == dunhill,
(
"yellow", "dunhill"))
problem
.addConstraint(lambda milk: milk == 3, ["milk"])
problem
.addConstraint(lambda norwegian: norwegian == 1, ["norwegian"])
problem
.addConstraint(lambda blends, cat: next_to(blends, cat),
(
"blends", "cat"))
problem
.addConstraint(lambda horse, dunhill: next_to(horse, dunhill),
(
"horse", "dunhill"))
problem
.addConstraint(lambda bluemaster, beer: bluemaster == beer,
(
"bluemaster", "beer"))
problem
.addConstraint(lambda german, prince: german == prince,
(
"german","prince"))
problem
.addConstraint(lambda norwegian, blue: next_to(norwegian, blue),
(
"norwegian", "blue"))
problem
.addConstraint(lambda blends, water: next_to(blends, water),
(
"blends", "water"))

# now get a solution
solution
= problem.getSolution()

# get in the form we want
answer
= {}
for i in range(1,6):
answer[i]
= {}

for k,v in solution.items():
if k in colors:
answer[v][
'color'] = k
elif k in nats:
answer[v][
'nationality'] = k
elif k in bevs:
answer[v][
'beverage'] = k
elif k in smokes:
answer[v][
'smokes'] = k
elif k in pets:
answer[v][
'pet'] = k

print "**** Solution ****"
for i in range(1,6):
print i, answer[i]['color'],answer[i]['nationality'],\
answer[i][
'beverage'],answer[i]['smokes'], answer[i]['pet']

for i in range(1,6):
if answer[i]['pet'] == 'fish':
owner_of_fish
= answer[i]['nationality']
break
print
print "Answer to riddle question: who has the fish for a pet? - the", \
owner_of_fish
 
# end of script -------------------------------------------------------