Solving the HKG18 puzzle with org-mode
Posted on Mon 26 March 2018 by alex in geek
One of the traditions I like about Linaro's Connect event is the conference puzzle. Usually set by Dave Piggot they provide a challenge to your jet lagged brain. Full disclosure: I did not complete the puzzle in time. In fact when Dave explained it I realised the answer had been staring me in the face. However I thought a successful walk through would make for a more entertaining read ;-)
First the Puzzle:
Take the clues below and solve them. Once solved, figure out what the hex numbers mean and then you should be able to associate each of the clue solutions with their respective hex numbers.
Clue | Hex Number |
---|---|
Lava Ale Code | 1114DBA |
Be Google Roe | 114F6BE |
Natural Gin | 114F72A |
Pope Charger | 121EE50 |
Dolt And Hunk | 12264BC |
Monk Hops Net | 122D9D9 |
Is Enriched Tin | 123C1EF |
Bran Hearing Kin | 1245D6E |
Enter Slim Beer | 127B78E |
Herbal Cabbages | 1282FDD |
Jan Venom Hon Nun | 12853C5 |
A Cherry Skull | 1287B3C |
Each Noun Lands | 1298F0B |
Wave Zone Kits | 12A024C |
Avid Null Sorts | 12A5190 |
Handcars All Trim | 12C76DC |
Clues
It looks like all the clues are anagrams. I was lazy and just used the first online anagram solver that Google pointed me at. However we can automate this by combining org-mode with Python and the excellent Beautiful Soup library.
from bs4 import BeautifulSoup
import requests
import re
# ask internet to solve the puzzle
url="http://anagram-solver.net/%s" % (anagram.replace(" ", "%20"))
page=requests.get(url)
# fish out the answers
soup=BeautifulSoup(page.text)
answers=soup.find("ul", class_="answers")
for li in answers.find_all("li"):
result = li.text
# filter out non computer related or poor results
if result in ["Elmer Berstein", "Tim-Berners Lee", "Babbage Charles", "Calude Shannon"]:
continue
# filter out non proper names
if re.search("[a-z] [A-Z]", result):
break
return result
So with :var anagram=clues[2,0]
we get
Ada Lovelace
I admit the "if result in []" is a bit of hack.
Hex Numbers
The hex numbers could be anything. But lets first start by converting to something else.
Hex Prompt | Number |
---|---|
1114DBA | 17911226 |
114F6BE | 18151102 |
114F72A | 18151210 |
121EE50 | 19000912 |
12264BC | 19031228 |
122D9D9 | 19061209 |
123C1EF | 19120623 |
1245D6E | 19160430 |
127B78E | 19380110 |
1282FDD | 19410909 |
12853C5 | 19420101 |
1287B3C | 19430204 |
1298F0B | 19500811 |
12A024C | 19530316 |
12A5190 | 19550608 |
12C76DC | 19691228 |
The #+TBLFM: is $1='(identity remote(clues,@@#$2))::$2='(string-to-number $1 16)
This is where I went down a blind alley. The fact all they all had the top bit set made me think that Dave was giving a hint to the purpose of the hex number in the way many cryptic crosswords do (I know he is a fan of these). However the more obvious answer is that everyone in the list was born in the last millennium.
Looking up Birth Dates
Now I could go through all the names by hand and look up their birth dates but as we are automating things perhaps we can use computers for what they are good at. Unfortunately there isn't a simple web-api for looking up this stuff. However there is a project called DBpedia which takes Wikipedia's data and attempts to make it semantically useful. We can query this using a semantic query language called SparQL. If only I could call it from Emacs…
select distinct ?birthDate {
dbr:$name dbo:birthDate|dbp:birthDate ?birthDate
}
So calling with :var name="Ada_Lovelace"
we get
"birthDate"
1815-12-10
Of course it shouldn't be a surprise this exists. And in what I hope is a growing trend sparql-mode supports org-mode out of the box. The $name in the snippet is expanded from the passed in variables to the function. This makes it a general purpose lookup function we can use for all our names.
There are a couple of wrinkles. We need to format the name we are looking up with underscores to make a valid URL. Also the output spits out a header and possible multiple birth dates. We can solve this with a little wrapper function. It also introduces some rate limiting so we don't smash DBpedia's public SPARQL endpoint.
;; rate limit
(sleep-for 1)
;; do the query
(let* ((str (s-replace-all '((" " . "_") ("Von" . "von")) name))
(ret (eval
(car
(read-from-string
(format "(org-sbe get-dob (name $\"%s\"))" str))))))
(string-to-number (replace-regexp-in-string "-" "" (car (cdr (s-lines ret))))))
Calling with :var name="Ada Lovelace"
we get
18151210
Full Solution
So now we know what we are doing we need to solve all the puzzles and lookup the data. Fortunately org-mode's tables are fully functional spreadsheets except they are not limited to simple transformations. Each formula can be a fully realised bit of elisp, calling other source blocks as needed.
Clue | Solution | DOB |
---|---|---|
Herbal Cabbages | Charles Babbage | 17911226 |
Be Google Roe | George Boole | 18151102 |
Lava Ale Code | Ada Lovelace | 18151210 |
A Cherry Skull | Haskell Curry | 19000912 |
Jan Venom Hon Nun | John Von Neumann | 19031228 |
Pope Charger | Grace Hopper | 19061209 |
Natural Gin | Alan Turing | 19120623 |
Each Noun Lands | Claude Shannon | 19160430 |
Dolt And Hunk | Donald Knuth | 19380110 |
Is Enriched Tin | Dennis Ritchie | 19410909 |
Bran Hearing Kin | Brian Kernighan | 19420101 |
Monk Hops Net | Ken Thompson | 19430204 |
Wave Zone Kits | Steve Wozniak | 19500811 |
Handcars All Trim | Richard Stallman | 19530316 |
Enter Slim Beer | Tim Berners-Lee | 19550608 |
Avid Null Sorts | Linus Torvalds | 19691228 |
The #+TBLFM: is $1='(identity remote(clues,@@#$1))::$2='(org-sbe solve-anagram (anagram $$1))::$3='(org-sbe frob-dob (name $$2))
The hex numbers are helpfully sorted so as long as we sort the clues table by the looked up date of birth using M-x org-table-sort-lines we are good to go.