# Enigmatic Code

Programming Enigma Puzzles

## Enigma 1689: What’s my number?

From New Scientist #2856, 17th March 2012 [link]

Three very logical friends, Amy, Bob and Carol, sat in a circle wearing hats. Each hat had a number on it so all three could see the others’ numbers but not their own. They were told that the numbers were three different positive digits.

They each made a statement in turn and were told to raise their hands when they knew their own number. Amy said: “Carol’s number is greater than Bob’s.” No one raised their hand.

Bob then said: “The sum of Amy’s and Carol’s numbers is even.” On hearing this, Carol raised her hand. Even then Amy and Bob did not raise theirs. But, after a pause, and once it had become clear that Amy wasn’t going to raise her hand, Bob raised his and then Amy raised hers.

What were Amy’s, Bob’s and Carol’s numbers?

[enigma1689]

### 4 responses to “Enigma 1689: What’s my number?”

1. Jim Randell 14 March 2012 at 11:29 pm

The following Python program runs in 37ms.

It accumulates the possibilities in various ways and eliminates options that don’t match the conditions of the problem, until we are left with the solution.

```from itertools import permutations
from collections import defaultdict
from enigma import printf

ds = set(range(1, 10)) # possible digits

abc = defaultdict(set)
# consider the possibilities for B
for B in ds:
# A says: C > B
Cs = list(range(B + 1, 10))
# at this stage there must be multiple possibilities for C
if len(Cs) < 2: continue
# B says: A + C is even, i.e. A, C are both even or both odd
# at this point C can be determined, knowing A and B
ac = defaultdict(set) # infer C from A (for fixed B)
for A in ds:
if A == B: continue
for C in Cs:
if A == C: continue
if (A + C) % 2: continue
# so, we're only interested where a single value of C can be inferred
# record the outcomes from A's point of view
bc = defaultdict(set)
for A, Cs in ac.items():
if len(Cs) > 1: continue
C = Cs.pop()
if not bc: continue
# accumulate values where A cannot be inferred from B, C
for (B, C), As in bc.items():
if len(As) < 2: continue
for A in As:
# B can now be inferred from A, C
bc = defaultdict(set)
for (A, C), Bs in abc.items():
if len(Bs) > 1: continue
B = Bs.pop()
# and A can be inferred from B, C
for (B, C), As in bc.items():
if len(As) > 1: continue
A = As.pop()
printf("A={A} B={B} C={C}")
```

Solution: Amy’s number is 6. Bob’s number is 7. Carol’s number is 8.

2. Tessa Fullwood 22 March 2012 at 11:02 am

Great stuff. I just wonder in line 5, if the range is 1-9, I was calculating on one positive digit, as in the first paragraph of the question.

• Jim Randell 22 March 2012 at 1:44 pm

In Python the range(a, b) function generates numbers from a to b but not including the endpoint. So range(1, 10) produces [1, 2, 3, 4, 5, 6, 7, 8, 9]. It’s handy when you’re indexing into an array of length n – you can just use range(n) – but at other times it can be annoying!

This is one of the rare cases in which Perl is more readable than Python. The equivalent Perl expression is simply 1..9.

• Jim Randell 19 April 2012 at 8:26 am

In fact I’ve introduced an irange() (inclusive range) function to my enigma.py module precisely for when you want an iterator that includes both its endpoints. If you use that you can use irange(1, 9) to get [1, 2, 3, 4, 5, 6, 7, 8, 9].