zebra_puzzle.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from itertools import permutations

# ---- The Introduction ----
# 1. There are five houses.
# 2. The Englishman lives in the red house.
# 3. The Spaniard owns the dog.
# 4. Coffee is drunk in the green house.
# 5. The Ukrainian drinks tea.
# 6. The green house is immediately to the right of the ivory house.
# 7. The Old Gold smoker owns snails.
# 8. Kools are smoked in the yellow house.
# 9. Milk is drunk in the middle house.
# 10. The Norwegian lives in the first house.
# 11. The man who smokes Chesterfields lives in the house next to the man with the fox.
# 12. Kools are smoked in the house next to the house where the horse is kept.
# 13. The Lucky Strike smoker drinks orange juice.
# 14. The Japanese smokes Parliaments.
# 15. The Norwegian lives next to the blue house.
# 
# Each of the five houses is painted a different color, and their
# inhabitants are of different national extractions, own different pets,
# drink different beverages and smoke different brands of cigarets.
# 
# Which of the residents drinks water?
# Who owns the zebra?

def next_to(x, y):
    """Is position x next to position y?"""
    return abs(x - y) == 1

def right(x, y):
    """Is position y to the right of position x?"""
    return y - x == 1

def solution():
    residents = [englishman, spaniard, ukrainian, norwegian, japanese] = range(5)
    perms = list(permutations(residents))
    answers = next(
        (zebra, water)
        for (first, second, middle, fourth, last) in perms
        if norwegian is first
        for (red, green, yellow, blue, ivory) in perms
        if next_to(norwegian, blue)
        if right(ivory, green)
        if red is englishman
        for (dog, fox, horse, snails, zebra) in perms
        if dog is spaniard
        for (coffee, tea, orange_juice, milk, water) in perms
        if milk is middle
        if green is coffee
        if ukrainian is tea
        for (old_gold, chesterfields, lucky_strike, parliaments, kools) in perms
        if old_gold is snails
        if kools is yellow
        if next_to(chesterfields, fox)
        if next_to(kools, horse)
        if lucky_strike is orange_juice
        if japanese is parliaments
    )

    names = ['Englishman', 'Spaniard', 'Ukrainian', 'Norwegian', 'Japanese']
    return ('It is the %s who drinks the water.\n'
            'The %s keeps the zebra.') % tuple(names[nat] for nat in answers)

@cds-amal and @hop think this looks great

Comments

I was bashing my head against this for weeks, and made several failed attempts. Did a lot of these puzzles with pen and paper when I was younger, to the point where I found them extremely formulaic - perhaps was trying too hard to translate the pen and paper approach literally.

I finally found some guidance from codecademy... this is an adaptation of their approach. It's kind of brute forcey but the numbers involved are quite small and I find the code quite user friendly. Learned a few things :)

helenst commented 14 July 2016 at 14:36 UTC

You're not logged in right now. Please login via GitHub to comment