Enigmatic Code

Programming Enigma Puzzles

Enigma 1069: Time for elevenses

From New Scientist #2225, 12th February 2000 [link]

I have a cube. On each of its faces is a digit, the style of writing being rather like the display on a calculator. I hold the cube to look at one of its faces from the front and then, keeping the upper and lower faces horizontal, I swivel the cube around and note the digits which I see (all seemingly the right way up) and hence I read off a four-figure number which is divisible by eleven.

Now I repeat the process starting this time looking from the front at one of the faces which was horizontal in the previous manoeuvre. Once again I read off a four-figure number which is divisible by eleven.

Now I start again with the cube in exactly the same position as it was at the start of the first process. This time I keep the left-hand and right-hand faces vertical and I swivel the cube around. Once again I read a four-figure number which is divisible by eleven and also by three odd integers less than eleven.

What was that last four figure number?


One response to “Enigma 1069: Time for elevenses

  1. Jim Randell 26 March 2018 at 7:39 am

    (See also: Enigma 362).

    There are many possibilities for the exact sequence of moves used in the puzzle, this code tries them all, and finds several solutions, but the third number (and the first number) are the same in all instances. There are two layouts of the cube which mirror each other.

    This Python code runs in 765ms.

    Run: [ @repl.it ]

    from collections import defaultdict
    from itertools import product
    from enigma import irange, icount, nsplit, update, printf
    X = None
    # map digits (0-9) to patterns (0, 1, 2, 3, 4, 5, 6, 7, 8) and orientation
    digits = {
      #   0- 1- 2- 3- quarter turns
      0: (0, 0, 0, 0),
      1: (1, X, 1, X),
      2: (2, X, 2, X),
      3: (3, X, X, X),
      4: (4, X, X, X),
      5: (5, X, 5, X),
      6: (6, X, 9, X), # pattern for 9 is the same as pattern for 6
      7: (7, X, X, X),
      8: (8, X, 8, X),
    # map each <digit> to a (<pattern>, <rotation>)
    pattern = dict()
    for (p, ds) in digits.items():
      for (r, d) in enumerate(ds):
        if d is None or d in pattern: continue
        pattern[d] = (p, r)
    # a cube is a sequence of (<pattern>, <rotation>) pairs for faces F, B, U, D, L, R
    (F, B, U, D, L, R) = (0, 1, 2, 3, 4, 5)
    # the empty cube
    cube0 = [None] * 6
    # available moves on the cube
    _moves = {
      'F': ((F, 1), (B, 3), (L, 1), (R, 1), (D, 1), (U, 1)),
      'B': ((F, 3), (B, 1), (R, 3), (L, 3), (U, 3), (D, 3)), # B = FFF
      'U': ((R, 0), (L, 2), (U, 1), (D, 3), (F, 0), (B, 2)),
      'D': ((L, 0), (R, 2), (U, 3), (D, 1), (B, 2), (F, 0)), # D = UUU
      'L': ((U, 0), (D, 0), (B, 0), (F, 0), (L, 1), (R, 3)),
      'R': ((D, 0), (U, 0), (F, 0), (B, 0), (L, 3), (R, 1)), # R = LLL
    # make a move on the cube
    def move(cube, m):
      c = list()
      for (f, r) in _moves[m]:
        if cube[f] is None:
          x = None
          (p, q) = cube[f]
          x = (p, (q + r) % 4)
      return c
    # apply a sequence of moves to a cube
    # * = read a digit from the front face
    def moves(seq, cube):
      ds = list()
      for m in seq:
        if m == "*":
          # * = read the front face as a digit
          if cube[F] is None:
            # reading an unassigned face
            (p, q) = cube[F]
            d = digits[p][q]
            # reading a non-digit
            if d is None: return
          # apply the move
          cube = move(cube, m)
      # retun the rotated cube, and any digits read off
      return (cube, ds)
    # find the cube that matches a given sequence of moves against digits ds
    def match(seq, ds, cube):
      ds = list(ds)
      for m in seq:
        if m == '*':
          # * = match the front face to the next digit
          d = ds.pop(0)
          if cube[F] is None:
            # if F is unassigned place the required pattern there
            cube = update(cube, [F], [pattern[d]])
            # check the pattern matches the next digit
            (p, q) = cube[F]
            if digits[p][q] != d: return
          # apply the move
          cube = move(cube, m)
      return cube
    # we read the third number with one of the following sequences:
    #  (*L*L*L*, *R*R*R*) [2]
    # we read the first number with one of:
    #  (*D*D*D*, *U*U*U*) [2]
    # and the second number with one of:
    #  (L, R) + ("", "F", "FF", "FFF") + (*D*D*D*, *U*U*U*) [2.4.2 = 16]
    # so 2.2.16 = 64 possible sequences altogether
    # 4-digit multiples of 11
    m11s = list(irange(1001, 9999, step=11))
    # accumulate results by n3
    ans = defaultdict(int)
    # consider possible values for the third number
    # which must be a multiple of 11
    for n3 in m11s:
      # and also a multiple of exactly 3 of 1, 3, 5, 7, 9
      if icount((3, 5, 7, 9), (lambda d: n3 % d == 0), 3) != 2: continue
      # consider possible sequences that give this number
      # and return the cube to the original orientation
      for seq3 in ["*L*L*L*L", "*R*R*R*R"]:
        # find a cube that matches
        cube3 = match(seq3, nsplit(n3, 4), cube0)
        if cube3 is None: continue
        # now consider possible values for the first number
        # which must be a multiple of 11
        for n1 in m11s:
          # consider possible sequences that will give this number
          # and return the cube to the original orientation
          for seq1 in ["*D*D*D*D", "*U*U*U*U"]:
            # find a cube that matches
            cube1 = match(seq1, nsplit(n1, 4), cube3)
            if cube1 is None: continue
            # consider possible starting positions and sequences for the second number
            for (s1, s2, s3) in product(["L", "R"], ["", "F", "FF", "FFF"], ["*D*D*D*", "*U*U*U*"]):
              seq2 = s1 + s2 + s3
              # read the digits from the cube
              r = moves(seq2, cube1)
              if r is None: continue
              (_, ds) = r
              # check the digits read off match a multiple of 11
              for n2 in m11s:
                if all(a is None or a == b for (a, b) in zip(ds, nsplit(n2, 4))):
                  ans[n3] += 1
                  # output a solution
                  printf("[n1={n1} n2={n2} n3={n3}, cube={cube1}, seq1={seq1} seq2={seq2} seq3={seq3}]", seq1=seq1[:-1], seq3=seq3[:-1])
    # output accumulated results
    for (n3, v) in ans.items():
      printf("n3 = {n3} [{v} solutions]")

    Solution: The third four digit number is 9625.

    9625 is a multiple of 1, 5, 7, and 11.

    The first number is 9020, and the second number is 5060 or 6050 (depending on the exact sequence of moves used, and the layout of the cube).

    The cube has a “6/9” pattern on the front (initially reading as a 9) and “2” pattern on the back. The up and down face have a “6/9” and “5” pattern on. And the left and right faces both have a “0” pattern on.

    The puzzle relies on the fact that the “0” pattern can be interpreted as a 0 digit in any quarter-turn orientation (not just upright and upside-down) – the puzzle says the digits are “rather like” 7-segment digits on a calculator, so we can suppose they are somewhat squarer. If you modify the declaration for the pattern “0” at line 10 to only represent 0 in 0- and 2- quarter turns you will find there are no solutions.

    Reading the first and third numbers provide enough information to fully describe all faces of the cube.

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: