Chapter 99
Can You Decode The Four Secret Messages?
Four coded messages arrive, each under a different scheme, said to run from easiest to hardest. Decode all four.
A zsnw kmuuwkkxmddq kgdnwv lzw XanwLzajlqWayzl Javvdwj!
xckik acvlbeg oz mmqn xnlautw. gzag, mwcht, kbjzh ulw cpeq edr mom dhqx lksxlioil?
hy vg nw rh ev pr is or tf?
(a line of smiley-face emoji)
The Riddler, FiveThirtyEight(original post)
Solution
Each message is a classic substitution, and the recurring key, hidden in plain sight, is the word fivethirtyeight.
First, a Caesar shift. Every letter is moved a fixed number of places along the alphabet. Trying shifts, the text resolves when each letter is moved back (equivalently forward ): AI, zh, and so on.
Second, a Vigenère cipher, a Caesar shift whose amount cycles through a keyword. With keyword fivethirtyeight, the first letter shifts back by f ( places), the next by i (), and so on, repeating.
Third, a Playfair cipher. The keyword (with repeats and j dropped, so fivethryg) fills a square, then the rest of the alphabet: Each pair of letters is decoded by its place in the square: same row, shift left; same column, shift up; otherwise swap to the other two corners of their rectangle.
Fourth, an emoji substitution. The smiley faces are simply the alphabet in Unicode order, the first codepoint standing for a, the next for b, and so on from U+1F600.
Decoding all four gives the answer:
The computation
Encode each cipher’s rule and run the actual ciphertext through it; the printed plaintext is the answer, not a restatement of it.
def caesar(s, k):
f = lambda c: chr((ord(c.lower()) - 97 + k) % 26 + 97) if c.isalpha() else c
return ''.join(f(c) for c in s)
def vigenere(s, key):
out, i = [], 0
for c in s:
if c.isalpha():
out.append(chr((ord(c.lower()) - 97 - (ord(key[i % len(key)]) - 97)) % 26 + 97))
i += 1
else:
out.append(c)
return ''.join(out)
def playfair(s, key):
grid = []
for c in key + 'abcdefghiklmnopqrstuvwxyz':
c = 'i' if c == 'j' else c
if c not in grid: grid.append(c)
pos = {c: (i // 5, i % 5) for i, c in enumerate(grid)}
xs = [c for c in s.lower() if c.isalpha()]
out = []
for a, b in zip(xs[::2], xs[1::2]):
(ra, ca), (rb, cb) = pos[a], pos[b]
if ra == rb: out += [grid[ra*5 + (ca-1) % 5], grid[rb*5 + (cb-1) % 5]]
elif ca == cb: out += [grid[((ra-1) % 5)*5 + ca], grid[((rb-1) % 5)*5 + cb]]
else: out += [grid[ra*5 + cb], grid[rb*5 + ca]]
return ''.join(out)
def emoji(s):
return ''.join(chr(97 + ord(c) - 0x1F600) if 0x1F600 <= ord(c) < 0x1F61A else c
for c in s)
m1 = "A zsnw kmuuwkkxmddq kgdnwv lzw XanwLzajlqWayzl Javvdwj!"
m2 = ("xckik acvlbeg oz mmqn xnlautw. gzag, mwcht, kbjzh "
"ulw cpeq edr mom dhqx lksxlioil?")
m3 = "hy vg nw rh ev pr is or tf?"
m4 = (
"\U0001F60E\U0001F60A, \U0001F613\U0001F607\U0001F600"
"\U0001F613'\U0001F612 \U0001F608\U0001F613. \U0001F60D"
"\U0001F60E\U0001F616 \U0001F606\U0001F604\U0001F613 "
"\U0001F601\U0001F600\U0001F602\U0001F60A \U0001F613"
"\U0001F60E \U0001F616\U0001F60E\U0001F611\U0001F60A. "
"\U0001F607\U0001F604\U0001F618, \U0001F600\U0001F613 "
"\U0001F60B\U0001F604\U0001F600\U0001F612\U0001F613 "
"\U0001F608\U0001F613'\U0001F612 \U0001F605\U0001F611"
"\U0001F608\U0001F603\U0001F600\U0001F618."
)
print(caesar(m1, 8))
print(vigenere(m2, "fivethirtyeight"))
print(playfair(m3, "fivethirtyeight"))
print(emoji(m4))
# i have successfully solved the fivethirtyeight riddler!
# super tuesday is this tuesday. cruz, trump, rubio who
# will win the most delegates?
# areyouhavingfunyet
# ok, that's it. now get back to work. hey, at least it's friday.