# Enigmatic Code

Programming Enigma Puzzles

## Enigma 339a: Christmas dice

From New Scientist #1487, 19th December 1985 [link]

During last Christmas’s intellectual activity — backgammon, ludo, and so on — Pam complained about the unfairness of using a pair of ordinary dice.

“With this pair,” she said, “you can only throw a total from 2 up to 12. But I should like to be able to throw any whole number from 2 up to much more than that. And with this pair I am much more likely to throw some totals that others — I get 7, for instance, six times as often as I get 12. What I want is a pair of dice which will throw every possible total with equal probability. And finally, there’s a 4 on each of these dice, and I object to square numbers. I don’t mind 1 — I don’t think of 1 as really square — but I don’t like 4 and I would equally object to 9 or 16 or 25 or 36.”

So, I have designed a special pair of dice for Pam’s Christmas present this year, which, I am glad to say, meets her wishes entirely. They are six-sided dice of the ordinary shape, with a positive whole number on each face, and they are equally likely to throw any total from 2 to 37 inclusive.

What are the numbers on the faces of each die, please?

There are now 950 Enigma puzzles available on the site.

[enigma339a] [enigma339]

### 2 responses to “Enigma 339a: Christmas dice”

1. Jim Randell 1 April 2016 at 7:58 am

This Python 3 program runs in 46ms.

```from enigma import irange, diff, printf

# generate dice (d1, d2)
# n = how many numbers on each die
# ds = (d1, d2) numbers on dice (so far)
# ss = numbers that can be thrown (so far)
# ns = numbers that remain to be thrown
# invalid = numbers that can't appear on a die
def dice(n, ds, ss, ns, invalid):
(d1, d2) = ds
# check ordering
if len(d1) == len(d2) and d2 < d1: return
# are we done?
if not ns:
yield ds
else:
# make the next number
x = ns[0]
for (d1, d2, r) in ((d1, d2, 1), (d2, d1, -1)):
if len(d1) < n:
# choose a number on the other die
for n2 in d2:
# so the number to add to the first die is ...
n1 = x - n2
# check it's valid
if n1 in invalid: continue
# and the numbers that makes with the second die are...
s = list(n1 + y for y in d2)
# check there are no duplicate values
if ss.intersection(s): continue
# and try to finish the problem...
yield from dice(n, (d1 + [n1], d2)[::r], ss.union(s), diff(ns, s), invalid)

squares = (4, 9, 16, 25, 36)

for (d1, d2) in dice(6, ([1], [1]), set([2]), irange(3, 37), set(squares)):
printf("{d1} x {d2}")
```

Solution: One die has faces (1, 2, 7, 8, 13, 14) the other has faces (1, 3, 5, 19, 21, 23).

There are also 6 other pairs of dice that can make all the numbers from 2 to 37, but that include non-unity square numbers on their faces:

(1, 2, 3, 4, 5, 6) (1, 7, 13, 19, 25, 31)
(1, 2, 3, 19, 20, 21) (1, 4, 7, 10, 13, 16)
(1, 2, 3, 10, 11, 12) (1, 4, 7, 19, 22, 25)
(1, 2, 3, 7, 8, 9) (1, 4, 13, 16, 25, 28)
(1, 2, 13, 14, 25, 26) (1, 3, 5, 7, 9, 11)
(1, 2, 5, 6, 9, 10) (1, 3, 13, 15, 25, 27)

And if we allow 0, instead of just positive numbers we can get three further solutions that don’t use non-unity squares:

(0, 1, 2, 6, 7, 8) (2, 5, 14, 17, 26, 29)
(0, 1, 2, 18, 19, 20) (2, 5, 8, 11, 14, 17)
(0, 2, 12, 14, 24, 26) (2, 3, 6, 7, 10, 11)

and 11 further solutions that do use non-unity squares:

(0, 1, 2, 3, 4, 5) (2, 8, 14, 20, 26, 32)
(0, 1, 2, 9, 10, 11) (2, 5, 8, 20, 23, 26)
(0, 1, 4, 5, 8, 9) (2, 4, 14, 16, 26, 28)
(0, 1, 6, 7, 12, 13) (2, 4, 6, 20, 22, 24)
(0, 1, 12, 13, 24, 25) (2, 4, 6, 8, 10, 12)
(0, 2, 4, 6, 8, 10) (2, 3, 14, 15, 26, 27)
(0, 2, 4, 18, 20, 22) (2, 3, 8, 9, 14, 15)
(0, 3, 6, 9, 12, 15) (2, 3, 4, 20, 21, 22)
(0, 3, 6, 18, 21, 24) (2, 3, 4, 11, 12, 13)
(0, 3, 12, 15, 24, 27) (2, 3, 4, 8, 9, 10)
(0, 6, 12, 18, 24, 30) (2, 3, 4, 5, 6, 7)

2. Brian Gladman 1 April 2016 at 3:04 pm
```# dice 'd1' and 'd2', numbers yet to be made in 'not_made'
# are there numbers still to be made?
# the number needed on a die to make the lowest unmade value
# try adding this number to the first die
if len(d1) < 6:
# compile the numbers it makes with those on the other die
s = set(x + nbr for x in d2)
# if all of these are needed, add it to the die and continue
yield from compose(d1 + [nbr], d2, not_made - s)
# now try to add it to the second die
if len(d2) < 6:
s = set(x + nbr for x in d1)