# Enigmatic Code

Programming Enigma Puzzles

## Enigma 1537: A name game

From New Scientist #2700, 21st March 2009 [link]

A school class consisted of Elsa, John, Marty, Paul, Sheila, Smack and Suzy. I explained to them that a 3-by-3 magic square consists of an array of nine different numbers such that any row, column or diagonal has the same sum. I asked each of them to make one using only whole numbers between 1 and 26 inclusive.

They each succeeded and I then asked them to replace their numbers by letters using A=1, B=2, C=3 and so on. Amazingly, in every case but one the child’s magic square contained the letters of their name.

Whose did not?

[enigma1537]

### 2 responses to “Enigma 1537: A name game”

1. Jim Randell 18 April 2012 at 12:31 pm

Here’s my original Perl solution. It runs in 1.8s.

```use strict;

use Enigma qw/distinct/;

# make a magic square
my (\$N11, \$N12, \$N13,
\$N21, \$N22, \$N23,
\$N31, \$N32, \$N33);

my (\$SUM, %LETTERS, %NAMES, %DONE, \$done);

%DONE = ();

for \$N11 (1..26) {
for \$N12 (1..26) {
next unless distinct(\$N12, \$N11);
for \$N13 (\$N11+1..26) {
next unless distinct(\$N13, \$N11, \$N12);
\$SUM = \$N11 + \$N12 + \$N13;
for \$N21 (1..26) {
next unless distinct(\$N21, \$N11, \$N12, \$N13);
\$N31 = \$SUM - \$N11 - \$N21;
next if \$N31 < 1 or \$N31 > 26 or \$N31 < \$N11 or \$N31 < \$N13;
next unless distinct(\$N31, \$N11, \$N12, \$N13, \$N21);
for \$N22 (1..26) {
next unless distinct(\$N22, \$N11, \$N12, \$N13, \$N21, \$N31);
next unless \$N13 + \$N22 + \$N31 == \$SUM;
\$N23 = \$SUM - \$N21 - \$N22;
next if \$N23 < 1 or \$N23 > 26;
next unless distinct(\$N23, \$N11, \$N12, \$N13, \$N21, \$N22, \$N31);
\$N32 = \$SUM - \$N12 - \$N22;
next if \$N32 < 1 or \$N32 > 26;
next unless distinct(\$N32, \$N11, \$N12, \$N13, \$N21, \$N22, \$N23, \$N31);
\$N33 = \$SUM - \$N13 - \$N23;
next if \$N33 < 1 or \$N33 > 26 or \$N33 < \$N11;
next unless distinct(\$N33, \$N11, \$N12, \$N13, \$N21, \$N22, \$N23, \$N31, \$N32);
next unless \$N11 + \$N22 + \$N33 == \$SUM;
next unless \$N31 + \$N32 + \$N33 == \$SUM;

# turn the digits into letters
%LETTERS = ();
for (\$N11, \$N12, \$N13, \$N21, \$N22, \$N23, \$N31, \$N32, \$N33) {
\$LETTERS{chr(64 + \$_)} = 1;
}
%NAMES = ();
for (qw/ELSA JOHN MARTY PAUL SHEILA SMACK SUZY/) {
\$done = 1;
for (split '', \$_) {
unless (\$LETTERS{\$_}) {
\$done = 0;
last;
}
}
next unless \$done;
\$DONE{\$_}++;
\$NAMES{\$_}++;
}
next unless keys %NAMES;

print "[\$SUM]\n";
print "\$N11,\$N12,\$N13\n\$N21,\$N22,\$N23\n\$N31,\$N32,\$N33\n";
print "[", join(',', sort keys %NAMES), "]\n\n";
}
}
}
}
}

for (qw/ELSA JOHN MARTY PAUL SHEILA SMACK SUZY/) {
printf "%s: %d\n", \$_, \$DONE{\$_} || 0;
}
```

Solution: Paul’s magic square does not contain the letters of his name.

• Jim Randell 18 April 2012 at 12:34 pm

And here’s a Python solution. It runs in 45ms.

It makes use of the fact that the centre number is a 3 x 3 magic square is one third the magic constant (see my comments in the Enigma 1680 posts for proof of this). I’ve also included a new irange() function (inclusive range) in the enigma.py library, for those cases when you want a range that includes both endpoints.

```from itertools import combinations
from enigma import irange, printf

ns = set(irange(1, 26))

names = ('ELSA', 'JOHN', 'MARTY', 'PAUL', 'SHEILA', 'SMACK', 'SUZY')

# consider the square:
#  a b c
#  d e f
#  g h i

# to eliminate duplicate squares we will assume b < d, f, h and a < c

count = dict((n, 0) for n in names)
# pick b
for b in irange(1, 23):
ns1 = ns.difference((b,))
# pick a and c
for (a, c) in combinations(ns1, 2):
if c < a: (a, c) = (c, a)
# determine the magic constant
s = a + b + c
# e is s / 3 (see solution to Enigma 1680)
(e, r) = divmod(s, 3)
if r > 0: continue

# and the other values follow
g = s - (c + e)
h = s - (b + e)
i = s - (a + e)
d = s - (a + g)
f = s - (c + i)
if not(b < d and b < f and b < h): continue

# check they are all different and valid
ns2 = ns1.difference((a, c, d, e, f, g, h, i))
if len(ns2) != 17: continue

# turn the numbers into letters
ls = set(chr(64 + n) for n in (a, b, c, d, e, f, g, h, i))

for n in names:
if set(n).issubset(ls): count[n] += 1

for (k, v) in count.items():
if v > 0: continue
printf("{k} occurs in {v} squares")
```