Enigmatic Code

Programming Enigma Puzzles

Enigma 999: Combined celebrations

From New Scientist #2154, 3rd October 1998 [link]

To celebrate next week’s 1000th edition of Enigma, we each made up an Enigma. Each one consisted of four clues leading to its own unique positive whole number answer. In each case none of the four clues was redundant. To avoid duplication, Keith made up his Enigma first and showed it to Susan before she made up hers.

The two Enigmas were meant to be printed side-by-side but the publishers have made a (rare) error and printed the clues in a string:

(A) It is a three-figure number;
(B) It is less than a thousand;
(C) It is a perfect square;
(D) It is a perfect cube;
(E) It has no repeated digits;
(F) The sum of its digits is a perfect square;
(G) The sum of its digits is a perfect cube;
(H) The sum of all the digits which are odd in Keith’s answer is the same as the sum of all the digits which are odd in Susan’s.

Which four clues should have formed Keith’s Enigma, and what was the answer to Susan’s?

There are now 1300 Enigma puzzles available on the site (or at least 1300 posts in the enigma category). There are 492 Enigma puzzles remaining to post.

There are currently also 76 puzzles from the Tantalizer series, 75 from the Puzzle series and 13 from the new Puzzle # series of puzzles that have been published in New Scientist which together cover puzzles from 1975 to 2019 (albeit with some gaps).

I also notice that the enigma.py library is now 10 years old (according to the header in the file – the creation date given coincides with me buying a book on Python). In those 10 years it has grown considerably, in both functionality and size. I’m considering doing a few articles focussed on specific functionality that is available in the library.

[enigma999]

2 responses to “Enigma 999: Combined celebrations

  1. Jim Randell 2 August 2019 at 8:43 am

    None of the statements are redundant, so (A) and (B) cannot appear in the same set of statements (as (A) implies (B)). Hence one of them must appear in each set, which means one of the numbers is 3-digit and the other is less than a thousand. This gives us an upper bound on the numbers we are looking for.

    The condition (H) must be part of Susan’s puzzle. So we can solve Keith’s puzzle first using 4 of the conditions (A)-(G), and then check that the remaining 3 conditions with the additional condition (H) give a viable puzzle for Susan.

    This Python program runs in 203ms.

    Run: [ @repl.it ]

    from enigma import is_square, is_cube, first, subsets, irange, nsplit, diff, join, printf
    
    # the conditions (except H), each take the number <n> and digits <ds>
    conditions = dict(
      # A: "it is a three-figure number"
      A=(lambda n, ds: len(ds) == 3),
      # B: "it is less than a thousand
      B=(lambda n, ds: n < 1000),
      # C: "it is a perfect square"
      C=(lambda n, ds: is_square(n)),
      # D: "it is a perfect cube"
      D=(lambda n, ds: is_cube(n)),
      # E: "it has no repeated digits"
      E=(lambda n, ds: len(set(ds)) == len(ds)),
      # F: "the sum of its digits is a perfect square"
      F=(lambda n, ds: is_square(sum(ds))),
      # G: "the sum of its digits is a perfect cube"
      G=(lambda n, ds: is_cube(sum(ds))),
    )
    
    # find solutions for the conditions in the specified range
    def solve(fns, a, b):
      for n in irange(a, b):
        ds = nsplit(n)
        if all(fn(n, ds) for fn in fns):
          yield n
    
    # find unique solution for conditions in fns
    def unique(fns, a=1, b=999):
      ns = first(solve(fns, a, b), 2)
      # is there a unique solution?
      return (ns[0] if len(ns) == 1 else None)
    
    # look for redundant conditions
    def is_redundant(fns, a=1, b=2000):
      return any(unique(fns[:i] + fns[i + 1:], a, b) is not None for (i, _) in enumerate(fns))
    
    # sum of odd digits in ds
    odds = lambda ds: sum(d for d in ds if d % 2 == 1)
    
    # choose 4 of the conditions for one of the puzzles for Keith
    for ks in subsets(conditions.keys(), size=4):
      # must have exactly one of A and B
      if not(('A' in ks) ^ ('B' in ks)): continue
    
      # solve Keith's puzzle
      kfns = list(conditions[k] for k in ks)
      K = unique(kfns)
      if K is None: continue
      
      # Susan has the remaining conditions
      sfns = list(v for (k, v) in conditions.items() if k not in ks)
      # and also condition H
      s = odds(nsplit(K))
      sfns.append(lambda n, ds: odds(ds) == s)
    
      # solve Susan's puzzle
      S = unique(sfns)
      if S is None: continue
    
      # check none of the selected conditions are redundant
      if is_redundant(sfns) or is_redundant(kfns): continue
    
      # output solution
      printf("Keith: {ks} -> {K}; Susan: {ss}H -> {S}", ks=join(ks), ss=join(diff(conditions.keys(), ks)))
    

    Solution: Keith’s puzzle uses clues (B) (D) (F) (G). The answer to Susan’s puzzle is 841.

    Keith’s puzzle was:

    (B) It is less than a thousand;
    (D) It is a perfect cube;
    (F) The sum of its digits is a perfect square;
    (G) The sum of its digits is a perfect cube.

    The unique solution to this puzzle is (a bit disappointingly) 1.

    None of the clues is redundant:

    without (B) 1000 would be a solution;
    without (D) 10 would be a solution;
    without (F) 8 would be a solution;
    without (G) 27 would be a solution.

    Susan’s puzzle was:

    (A) It is a three-figure number;
    (C) It is a perfect square;
    (E) It has no repeated digits;
    (H) The sum of the odd digits is the same as the sum of the odd digits in Keith’s puzzle.

    The unique solution to this puzzle is 841 (= 29²).

    None of the clues is redundant:

    without (A) 1 would be a solution;
    without (C) 102 would be a solution;
    without (E) 100 would be a solution;
    without (H) 169 would be a solution.

    Originally I thought there was a further solution to the Enigma:

    Keith = (B) (C) (E) (G) (solution = 1)
    Susan = (A) (D) (F) (H) (solution = 216)

    But in this case Susan’s puzzle has the same unique solution if only conditions (A) (D) (H) are considered, so the condition (F) is redundant, and hence this is not a further solution.

    In the program we check to see if any of the conditions can be removed while leaving a unique solution (we check numbers up to 2000, because conditions (A) and (B) can be removed), if we do not find any additional solution then the removed condition is redundant.

  2. Jim Olson 3 August 2019 at 11:09 pm

    Thank you for all your efforts on this site. It has provided years of enjoyment.

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: