# Enigmatic Code

Programming Enigma Puzzles

## Enigma 311: Three score years and ten

From New Scientist #1459, 6th June 1985 [link]

The ages of George’s four daughters add up to 70. Amanda says that the exact figures are 8, 16, 21, and 25. But Brenda says that Celia is 15. Delia, on the other hand, says that Celia is 18.

This is all very confusing, until you know about a strange family habit. It is to state one’s own age correctly but to overstate the age of anyone older and to understate the age of anyone younger.

Even after making all possible deductions so far, you cannot work out the age of each daughter. For that you need a bit more information, for instance the number of years separating Belinda and Celia.

Please supply the name and age of the four.

There are now 894 Enigma puzzles on the site, and I think this is around half of all the Enigma puzzles published in New Scientist, from Enigma 1 in February 1979 to Enigma 1780 in December 2013.

To help me keep on top of posting the remaining Enigma puzzles I’m going to change the posting schedule to two puzzles a week, one on Friday and one on Monday. Which means, if I can keep sourcing the puzzles, I will have enough to last another 8.6 years!

[enigma311]

### One response to “Enigma 311: Three score years and ten”

1. Jim Randell 18 September 2015 at 12:39 pm

This Python 2 program runs in 87ms. (I used the filter_unique() function from the enigma.py library, rather than spin the possibilities into a collections.defaultdict()).

```from enigma import irange, diff, filter_unique, printf

# decompose total <t> into <n> numbers
def decompose(t, n, s=[]):
if n == 1:
yield s + [t]
else:
for x in irange(1, t - n + 1):
for r in decompose(t - x, n - 1, s + [x]): yield r

# would someone of age <a> say that sisters whose actual ages are <rs> are <ss>?
def check(a, rs, ss):
if not rs:
return True
else:
# consider the next real age
r = rs[0]
# find a corresponding stated age
for (i, s) in enumerate(ss):
if (s < r < a or s > r > a) and check(a, rs[1:], ss[:i] + ss[i + 1:]):
return True
# it is not possible
return False

# A says the ages are (8, 16, 21, 25), so one of these is her age
ages = (8, 16, 21, 25)

# accumulate possible ages
r = list()
for A in ages:
# and possible remaining ages
for (B, C, D) in decompose(70 - A, 3):

# check A's statement
if not check(A, [B, C, D], diff(ages, [A])): continue

# B says that C is 15
if not check(B, [C], [15]): continue

# D says that C is 18
if not check(D, [C], [18]): continue

printf("[A={A} B={B} C={C} D={D}]")
r.append((A, B, C, D))

# there should be multiple possibilities
assert len(r) > 1

# but the result can be uniquely determined from the difference in age between B and C
fn = lambda (A, B, C, D): abs(B - C)
(r, _) = filter_unique(r, fn)

# output the results
for (A, B, C, D) in r:
printf("A={A} B={B} C={C} D={D}")
```

Solution: Amanda is 16. Brenda is 24. Celia is 17. Delia is 13.

The program presented does not run under Python 3 because it uses Tuple Parameter Unpacking which was removed from Python 3 (see PEP 3113). I think this was a bit of a shame, because although you can easily work around the removal it does make the resulting code look messy. (On the plus side if you do modify the program to run under Python 3 you can use the yield from construct in the recursive call in decompose() and stop it from working in Python 2).

Before we are given the difference between B and C there are 5 possibilities:

A=16, B=22, C=17, D=15 (B−C=5)
A=16, B=23, C=17, D=14 (B−C=6)
A=16, B=24, C=17, D=13 (B−C=7)
A=21, B=22, C=17, D=10 (B−C=5)
A=21, B=23, C=17, D=9 (B−C=6)