Enigmatic Code

Programming Enigma Puzzles

Enigma 1764: Secret passages

From New Scientist #2932, 31st August 2013 [link] [link]

Kathryn and her school friends have been using a Lorenz-type code to pass covert messages to each other. Each letter is expressed as a five-digit binary number such that A = 1 = 00001, M = 13 = 01101 and so on, but other symbols are represented by 00000 and by 11011 upwards. A fixed letter, say M, is chosen as a “coder”, known only to the sender and receiver. To transmit a letter, say D, it is added to the coder by the “exclusive-NOR” rule:

1 + 1 = 1,
1 + 0 = 0,
0 + 1 = 0,
0 + 0 = 1.

So, for example, D + M = 00100 + 01101 = 10110 = V. When the sent letter V is added by the recipient to the coder M, the original letter reappears: 10110 + 01101 = 00100.

She has sent her name to her friends as seven letters. KATHRYN and its coded version together consist of 14 different letters, so what was the coded version?

[enigma1764]

4 responses to “Enigma 1764: Secret passages

  1. Jim Randell 28 August 2013 at 6:25 pm

    This Python program runs in 32ms.

    from enigma import irange, printf
    
    # maps: letters -> code, code -> letters
    (l2c, c2l) = (dict(), dict())
    for c in irange(1, 26):
      l = chr(64 + c)
      l2c[l] = c
      c2l[c] = l
    
    # encode/decode a message with the given key
    def code(k, text):
      return ''.join(c2l.get((l2c[l] ^ k) ^ 0b11111, '?') for l in text)
    
    # message plaintext
    text = 'KATHRYN'
    
    # choose a coding letter
    for (k, v) in c2l.items():
      # encode the message
      msg = code(k, text)
      # skip encodings with non-letters
      if '?' in msg: continue
      # no shared letters in the text and the message
      if set(text).intersection(msg): continue
    
      printf("message={msg} [key={v}]")
    

    Solution: The coded version of KATHRYN is PZOSIBU.

    • Jim Randell 28 August 2013 at 10:31 pm

      And here’s a different approach. Instead of considering each coding key letter and looking at what the encoded message is, it considers each letter of the plaintext and eliminates coding letters that won’t work for that letter. So it only needs to encode 47 letters rather than 26×7 = 182 letters (not that it makes much difference on a problem of this size).

      from enigma import printf
      
      # encode/decode a letter l with the key k
      def encode(k, l):
        e = (ord(k) - 64) ^ (ord(l) - 64) ^ 0b11111
        return chr(e + 64) if 0 < e < 27 else '?'
      
      # plaintext
      text = 'KATHRYN'
      
      # encoded strings by k
      enc = dict((k, '') for k in 'BCDEFGIJLMOPQSUVWXZ')
      # consider each letter of the plaintext
      for l in text:
        # keep keys that encode to a valid unused letter
        for k in list(enc.keys()):
          e = encode(k, l)
          # remove encodings that have non-letters or share letters with the plaintext
          if e == '?' or e in text:
            del enc[k]
          else:
            enc[k] += e
        printf("[{l}] {n} keys, {ks}", n=len(enc), ks=' '.join(k + ':' + enc[k] for k in sorted(enc.keys())))
      
      for k in sorted(enc.keys()):
        printf("encoded={v} key={k}", v=enc[k])
      
  2. Brian Gladman 28 August 2013 at 8:01 pm
    # convert KATHRYN to its letter code form
    text = [ord(c) - ord('A') + 1 for c in 'KATHRYN']
    # now try all code values as the coder
    for key in range(1, 27):
      # form the cipher text
      cipher = [~(c ^ key) & 0x1f  for c in text]
      # check that its elements are in the code range for letters
      if all(0 < c < 27 for c in cipher):
        # now covert it to its letter form
        code_word = ''.join(chr(c + ord('A') - 1) for c in cipher)
        # and check that it shares no letters with KATHRYN
        if not (set('KATHRYN') & set(code_word)): 
          # output the code letter and the encoded version of KATHRYN
          letter = chr(key + ord('A') - 1)
          print('{:s} codes KATHRYN as {:s}.'.format(letter, code_word))
    
  3. arthurvause 28 August 2013 at 8:57 pm
    def encode(x,y):
      return chr( ((ord(x)-64)^(ord(y)-64))^31 +64)
    
    alphabet=set('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
    remainder=alphabet-set('KATHRYN')
    for key in alphabet:
      if all(encode(x,key) in remainder for x in 'KATHRYN'):
        print "key :",key ,", ", ''.join([encode(c,key) for c in 'KATHRYN'])
    

Leave a reply to Jim Randell Cancel reply

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