Added a new day 3, rearranged days
[ou-summer-of-code-2017.git] / 03-door-codes / door-codes-solution.ipynb
diff --git a/03-door-codes/door-codes-solution.ipynb b/03-door-codes/door-codes-solution.ipynb
new file mode 100644 (file)
index 0000000..3f39c49
--- /dev/null
@@ -0,0 +1,354 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Door codes\n",
+    "\n",
+    "After a fairly dull flight, you've finally arrived at your hotel. The good news is that the hotel has high-security electronic locks on the room doors. The bad news is that the staff are rather busy, and you think it will take a long time to get to your room.\n",
+    "\n",
+    "Luckily, you know how their system works. \n",
+    "\n",
+    "Each door in the hotel has a keyboard on the lock. You have to enter the correct two-letter code to get in to the room. Because the staff know that people won't remember the codes, they tell you a pass phrase you can use the generate the code from.\n",
+    "\n",
+    "There's a long queue for people to be told how to generate the code from the pass phrase. You were here last year and you still remember how to do it.\n",
+    "\n",
+    "You start with the first two letters of the pass phrase. This is the starting value of your code.\n",
+    "\n",
+    "Then, for each subsequent letter in the pass phrase, you:\n",
+    "1. \"Add\" the second letter of the code to the first letter of the code, replacing the first letter of the code\n",
+    "2. \"Add\" the current letter of the pass phrase to the second letter of the code, replacing the second letter of the code\n",
+    "3. Move on to the next letter of the pass phrase\n",
+    "\n",
+    "\"Adding\" letters is done by converting the letters to their position in the alphabet (starting at one), adding, then converting the number back to a letter. Numbers greater than 26 are \"wrapped around\". For instance, to add `u` + `k`, convert them to numbers (`u`=`21`, `k`=`11`), add them (`21` + `11` = `32`), then convert back to a letter (`32` is larger than 26, so it becomes `32` - 26 = `6`, which is `f`).\n",
+    "\n",
+    "Anything that isn't a lower-case letter is ignored.\n",
+    "\n",
+    "For example, to find the code from the pass phrase `the cat`, the code starts as being the first two letters `th`, then the subsequent letters are used to update the code as follows:\n",
+    "\n",
+    "| old code | code as<br>numbers | passphrase<br/>letter | number of<br/>letter | new first<br/>part of code |new second<br/>part of code | new code |\n",
+    "|:---|:---|:---|:---|:---|:---|:---|\n",
+    "| th | (20, 8) | e | 5 | 28 | 13 | bm |\n",
+    "| bm | (2, 13) | c | 3 | 15 | 16 | op |\n",
+    "| op | (15, 16) | a | 1 | 31 | 17 | eq |\n",
+    "| eq | (5, 17) | t | 20 | 22 | 37 | vk |\n",
+    "\n",
+    "So the final code is `vk`\n",
+    "\n",
+    "## Part 1\n",
+    "Your passphrase is \n",
+    "\"the traveller in the grey riding-coat, who called himself mr. melville, was contemplating the malice of which the gods are capable.\"\n",
+    "\n",
+    "**What is your door code?**"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 98,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "import string"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 99,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def o(letter):\n",
+    "    return ord(letter) - ord('a') + 1\n",
+    "\n",
+    "def c(number):\n",
+    "    return chr((number - 1) % 26 + ord('a'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 100,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def sanitise(phrase):\n",
+    "    return ''.join(l for l in phrase if l in string.ascii_lowercase)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 101,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def whash1(word, show_steps=False):\n",
+    "    if show_steps:\n",
+    "        print('| old code | code as<br>numbers | passphrase<br/>letter | number of<br/>letter | new first<br/>part of code |'\n",
+    "              'new second<br/>part of code | new code |')\n",
+    "        print('|:---|:---|:---|:---|:---|:---|:---|')\n",
+    "    h = list(word[:2])\n",
+    "    for l in word[2:]:\n",
+    "        if show_steps:\n",
+    "            h_before = h.copy()\n",
+    "        h[0] = c(o(h[0]) + o(h[1]))\n",
+    "        h[1] = c(o(h[1]) + o(l))\n",
+    "        if show_steps:\n",
+    "            print('| {} | {} | {} | {} | {} | {} | {} |'.format(\n",
+    "                  ''.join(h_before), \n",
+    "                  (o(h_before[0]), o(h_before[1])),\n",
+    "                  l, \n",
+    "                  o(l), \n",
+    "                  o(h_before[0]) + o(h_before[1]), \n",
+    "                  o(h_before[1]) + o(l), \n",
+    "                  ''.join(h)))\n",
+    "    return ''.join(h)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 102,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "| old code | code as<br>numbers | passphrase<br/>letter | number of<br/>letter | new first<br/>part of code |new second<br/>part of code | new code |\n",
+      "|:---|:---|:---|:---|:---|:---|:---|\n",
+      "| th | (20, 8) | e | 5 | 28 | 13 | bm |\n",
+      "| bm | (2, 13) | c | 3 | 15 | 16 | op |\n",
+      "| op | (15, 16) | a | 1 | 31 | 17 | eq |\n",
+      "| eq | (5, 17) | t | 20 | 22 | 37 | vk |\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "'vk'"
+      ]
+     },
+     "execution_count": 102,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "whash1(sanitise('the cat'), show_steps=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 103,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "passphrase = sanitise(\"the traveller in the grey riding-coat, who called himself mr. melville, was \"\n",
+    "                      \"contemplating the malice of which the gods are capable.\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 104,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'mc'"
+      ]
+     },
+     "execution_count": 104,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "whash1(passphrase)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "You arrive at your door but the passcode doesn't work!\n",
+    "\n",
+    "Looking at the lock, you see it's a different model from last time you were here. Reluctantly, you return to the hotel front desk and join the queue for instructions. \n",
+    "\n",
+    "Indeed, the algorithm has been changed, \"to improve security,\" they say.\n",
+    "\n",
+    "The new algorithm starts with the same initial value of the code for every passphrase. In in this case, the initial code is `ri`. The algorithm also uses two contants, `m1`= 5 and `m2` = 11.\n",
+    "\n",
+    "Then, for each letter in the pass phrase, the code is updated as follows:\n",
+    "\n",
+    "1. \"Add\" the second letter of the code to the first letter of the code \"multiplied by\" `m1`, replacing the first letter of the code\n",
+    "2. \"Add\" the current letter of the pass phrase to the second letter of the code \"multiplied by\" `m2`, replacing the second letter of the code\n",
+    "\n",
+    "\"Multiplying\" letters is done by converting the letters to their position in the alphabet (starting at one) and multiplying. For instance, to multiply `u` by 11, convert `u` to `21`, multiply by 11 (`21` × `11` = `231`), then convert back to a letter (`231` is larger than 26, so it becomes `22`, which is `w`).\n",
+    "\n",
+    "Again, anything that isn't a lower-case letter is ignored.\n",
+    "\n",
+    "For example, to find the code from the pass phrase `the cat`, the code starts as being the first two letters `th`, then the subsequent letters are used to update the code as follows:\n",
+    "\n",
+    "| old code | code as<br>numbers | passphrase<br/>letter | number of<br/>letter | new first<br/>part of code |new second<br/>part of code | new code |\n",
+    "|:---|:---|:---|:---|:---|:---|:---|\n",
+    "| ri | (18, 9) | t | 20 | 63 | 229 | ku |\n",
+    "| ku | (11, 21) | h | 8 | 116 | 109 | le |\n",
+    "| le | (12, 5) | e | 5 | 37 | 60 | kh |\n",
+    "| kh | (11, 8) | c | 3 | 51 | 41 | yo |\n",
+    "| yo | (25, 15) | a | 1 | 100 | 26 | vz |\n",
+    "| vz | (22, 26) | t | 20 | 152 | 246 | vl |\n",
+    "\n",
+    "So the final code is `pl`\n",
+    "\n",
+    "## Part 2\n",
+    "Your passphrase remains \"the traveller in the grey riding-coat, who called himself mr. melville, was contemplating the malice of which the gods are capable.\"\n",
+    "\n",
+    "Using this new algorithm, **what is your door code?**"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 105,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "(21, 231, 22, 'w')"
+      ]
+     },
+     "execution_count": 105,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "o('u'), o('u') * 11, (o('u') * 11 - 1) % 26, c(o('u') * 11)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 113,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def whash2(word, h0=None, m1=5, m2=11, show_steps=False):\n",
+    "    if show_steps:\n",
+    "        print('| old code | code as<br>numbers | passphrase<br/>letter | number of<br/>letter | new first<br/>part of code |'\n",
+    "              'new second<br/>part of code | new code |')\n",
+    "        print('|:---|:---|:---|:---|:---|:---|:---|')\n",
+    "    if h0 is None:\n",
+    "        h = list('ri')\n",
+    "    else:\n",
+    "        h = list(h0)\n",
+    "    for l in word:\n",
+    "        if show_steps:\n",
+    "            h_before = h.copy()\n",
+    "        h[0] = c(o(h[0]) + o(h[1]) * m1)\n",
+    "        h[1] = c(o(h[1]) + o(l) * m2)\n",
+    "        if show_steps:\n",
+    "            print('| {} | {} | {} | {} | {} | {} | {} |'.format(\n",
+    "                  ''.join(h_before), \n",
+    "                  (o(h_before[0]), o(h_before[1])),\n",
+    "                  l, \n",
+    "                  o(l), \n",
+    "                  o(h_before[0]) + o(h_before[1]) * m1, \n",
+    "                  o(h_before[1]) + o(l) * m2, \n",
+    "                  ''.join(h)))\n",
+    "    return ''.join(h)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 116,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "| old code | code as<br>numbers | passphrase<br/>letter | number of<br/>letter | new first<br/>part of code |new second<br/>part of code | new code |\n",
+      "|:---|:---|:---|:---|:---|:---|:---|\n",
+      "| ri | (18, 9) | t | 20 | 63 | 229 | ku |\n",
+      "| ku | (11, 21) | h | 8 | 116 | 109 | le |\n",
+      "| le | (12, 5) | e | 5 | 37 | 60 | kh |\n",
+      "| kh | (11, 8) | c | 3 | 51 | 41 | yo |\n",
+      "| yo | (25, 15) | a | 1 | 100 | 26 | vz |\n",
+      "| vz | (22, 26) | t | 20 | 152 | 246 | vl |\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "'vl'"
+      ]
+     },
+     "execution_count": 116,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "whash2(sanitise('the cat'), show_steps=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 117,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "'qb'"
+      ]
+     },
+     "execution_count": 117,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "whash2(passphrase)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.5.2+"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}