Got hillclimbing and simulated annealing searches working
[cipher-tools.git] / 2017 / 2017-challenge5.ipynb
index 31606534bda08aefa6e14c27a29168a896a1664d..39f3f8ff165b71f4c8771fd681975f67fcf6af2e 100644 (file)
@@ -15,6 +15,7 @@
     "%matplotlib inline\n",
     "\n",
     "from cipherbreak import *\n",
+    "from text_prettify import *\n",
     "\n",
     "ca = open('5a.ciphertext').read()\n",
     "cb = open('5b.ciphertext').read()"
    "metadata": {},
    "outputs": [],
    "source": [
-    "def tpack(text, width=100):\n",
-    "    lines = [text[0]]\n",
-    "    for word in text[1:]:\n",
-    "        if len(lines[-1]) + 1 + len(word) <= width:\n",
-    "            lines[-1] += (' ' + word)\n",
-    "        else:\n",
-    "            lines += [word]\n",
-    "    return lines"
+    "def tpack(text, width=100):\n",
+    "    lines = [text[0]]\n",
+    "    for word in text[1:]:\n",
+    "        if len(lines[-1]) + 1 + len(word) <= width:\n",
+    "            lines[-1] += (' ' + word)\n",
+    "        else:\n",
+    "            lines += [word]\n",
+    "    return lines"
    ]
   },
   {
@@ -55,7 +56,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -83,7 +84,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [
     {
@@ -92,7 +93,7 @@
        "(24, -1733.3180566179738)"
       ]
      },
-     "execution_count": 17,
+     "execution_count": 5,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [
     {
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [
     {
        "((17, 21, True), -1723.2877779777489)"
       ]
      },
-     "execution_count": 20,
+     "execution_count": 7,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [
     {
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [
     {
        "'etoainhsrdlumwycfgpbvkxjqz'"
       ]
      },
-     "execution_count": 11,
+     "execution_count": 9,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 10,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "'yrkfdlbqpoisjcumzeawhtxgnv'"
+       "'yrkfdlbqpoisjcumzeathwxgvn'"
       ]
      },
-     "execution_count": 13,
+     "execution_count": 10,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [
     {
        " 'K': 'o',\n",
        " 'L': 'n',\n",
        " 'M': 'c',\n",
-       " 'N': 'q',\n",
+       " 'N': 'z',\n",
        " 'O': 'd',\n",
        " 'P': 'r',\n",
        " 'Q': 's',\n",
        " 'R': 't',\n",
        " 'S': 'u',\n",
-       " 'T': 'k',\n",
+       " 'T': 'b',\n",
        " 'U': 'y',\n",
-       " 'V': 'z',\n",
-       " 'W': 'b',\n",
+       " 'V': 'q',\n",
+       " 'W': 'k',\n",
        " 'X': 'x',\n",
        " 'Y': 'e',\n",
        " 'Z': 'f'}"
       ]
      },
-     "execution_count": 14,
+     "execution_count": 11,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "mirbi mjnda ehisp noeai rrake daose lwuvi sclio oedgu tshed adotm iveth ereod exknu sthel nwils titan othao vshem ibhik egeeo vadoi ccedg utthe rehis geeoo nrios nmdem iodio dther eason thaop muwht npnno herrn nmhid geeor iosiw vedio dshel efton messi pegut snmet haopt ellsm eshea snvio dyesh nuldm nkeno athao vshem ibhik eclio oedth asmei oyhal eahik ethed nwume otjnd aeyio tedfr nmlno dnoad notvo nyhny shevo eygut thefa fthwh icter yisha ddeoa oiwik atbao istno elaot elaos nmere miaos aothe eches usrnn mitth egrat ashmu seuma tyise owrbc tedus aopio nther cnlbi lchig etaww acher aokeo tedao theoa oetee othwe oturb aisve dthem useum tntes tatio dthed nwume otasi utheo tawsn apues sthea mcera ilwac hersw hnnlr eillb hidmn kedno cerhi csash nuldo thike geeos urcra sedif terth edasw nkerb itgnd rumgu tatle ikesm eynod eraop yhite lseth ebdek elnce dnoen ddfei turey isifr aexer uooao prnuo dthre esade snfth elaot elwno sasta opnfi saopl ernyn fgliw viody hatet alest hitth emuse umtha ovyer eidde daoth ernmi ocera ndthe breid aoseq ueowe yyggy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg gyyyg yygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygyyg yyygy gggyg ggygg gyggg ygggy gggyg ggygg gyggg ygggy gggyg ggygg gyggg ygggy gggyg ggygg gyggg ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yygyy ygyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy ygyyy gyyyy gyyyg ggyyy yyggg yyygy yygyy ygyyy gyyyy gggyy gyyyg ygggy yyggg gyygg gyyyg ggyyg ggygy yygyy yygyg yygyy gyyyg yyyyy gygyy ggyyg yygyg yyyyg yyygg yggyg yygyy gyyyy ygyyg yyygy yyygy yygyy ygyyg yyygy gyyyg ygyyy yygyy ygygy gygyg yyygy yygyy ygygy gygyy gyygy yyyyg yyygy ygyyy ygyyg yyygy yyggg ggygg ggyyg yyyyy ggggg ygygy gyggg ggyyy gyyyg yyygy ggggy ygggy yyggg gyyyg yyyyg yyygy yygyy gyyyg ygygy yygyy yyygy yygyg ygygy gyyyg yyygy yygyy ygygy yyyyg yyyyy gygyy yygyy yygyy gyyyg yyygy yygyg yygyy ygyyy ygyyy gygyy ggygy yygyy ygyyy gyyyg ygyyy yygyy yyygy ygyyy gyyyy gyyyg yyygy ygyyy gygyy ygyyy gggyg yyygy gyyyg ygyyy gyygg gyygy yygyg yyyyy ggggy ygyyy gyggg yyggg ygyyy gyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy gyyyg yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yygyy ygygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg ygyyy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy yygyy gggyg ggygg gyggg ygggy gggyg ggygg gyggg ygggy gggyg ggygg gyggg ygggy gggyg ggygg gyggg yygyy ygyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyg yyahi keuse dgfnr thegl iwvta lesio dyfnr theyh ateno esatt nnvme iyhal etnfa puren utthi tthef raexe asste pionp richb wnowe ilaop theve bynrd fnrth ewach eraow hicte rfake yathn utjnd aeaim ontsu reyhe retnp noezt there ireno lbtyn cnssa galat aesis firis awios eegut ahike onade iyher enoen fthem asiod isfir isawi otell onnoe elsev onyse ather snapu essay allhe idfnr nlbmc aisee bnuth ere\n"
+      "mirki mjnda ehisp noeai rrabe daose lwuvi sclio oedgu tshed adotm iveth ereod exbnu sthel nwils titan othao vshem ikhib egeeo vadoi ccedg utthe rehis geeoo nrios nmdem iodio dther eason thaop muwht npnno herrn nmhid geeor iosiw vedio dshel efton messi pegut snmet haopt ellsm eshea snvio dyesh nuldm nbeno athao vshem ikhib eclio oedth asmei oyhal eahib ethed nwume otjnd aeyio tedfr nmlno dnoad notvo nyhny shevo eygut thefa fthwh icter yisha ddeoa oiwib atkao istno elaot elaos nmere miaos aothe eches usrnn mitth egrat ashmu seuma tyise owrkc tedus aopio nther cnlki lchig etaww acher aobeo tedao theoa oetee othwe oturk aisve dthem useum tntes tatio dthed nwume otasi utheo tawsn apues sthea mcera ilwac hersw hnnlr eillk hidmn bedno cerhi csash nuldo thibe geeos urcra sedif terth edasw nberk itgnd rumgu tatle ibesm eynod eraop yhite lseth ekdeb elnce dnoen ddfei turey isifr aexer uooao prnuo dthre esade snfth elaot elwno sasta opnfi saopl ernyn fgliw viody hatet alest hitth emuse umtha ovyer eidde daoth ernmi ocera ndthe kreid aosez ueowe yyggy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg gyyyg yygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygyyg yyygy gggyg ggygg gyggg ygggy gggyg ggygg gyggg ygggy gggyg ggygg gyggg ygggy gggyg ggygg gyggg ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yygyy ygyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy ygyyy gyyyy gyyyg ggyyy yyggg yyygy yygyy ygyyy gyyyy gggyy gyyyg ygggy yyggg gyygg gyyyg ggyyg ggygy yygyy yygyg yygyy gyyyg yyyyy gygyy ggyyg yygyg yyyyg yyygg yggyg yygyy gyyyy ygyyg yyygy yyygy yygyy ygyyg yyygy gyyyg ygyyy yygyy ygygy gygyg yyygy yygyy ygygy gygyy gyygy yyyyg yyygy ygyyy ygyyg yyygy yyggg ggygg ggyyg yyyyy ggggg ygygy gyggg ggyyy gyyyg yyygy ggggy ygggy yyggg gyyyg yyyyg yyygy yygyy gyyyg ygygy yygyy yyygy yygyg ygygy gyyyg yyygy yygyy ygygy yyyyg yyyyy gygyy yygyy yygyy gyyyg yyygy yygyg yygyy ygyyy ygyyy gygyy ggygy yygyy ygyyy gyyyg ygyyy yygyy yyygy ygyyy gyyyy gyyyg yyygy ygyyy gygyy ygyyy gggyg yyygy gyyyg ygyyy gyygg gyygy yygyg yyyyy ggggy ygyyy gyggg yyggg ygyyy gyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy gyyyg yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yyyyy yygyy ygygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg ygyyy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy gygyg ygygy yygyy gggyg ggygg gyggg ygggy gggyg ggygg gyggg ygggy gggyg ggygg gyggg ygggy gggyg ggygg gyggg yygyy ygyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyy gyyyg yyygy yygyy ygyyg yyahi beuse dgfnr thegl iwvta lesio dyfnr theyh ateno esatt nnvme iyhal etnfa puren utthi tthef raexe asste pionp richk wnowe ilaop theve kynrd fnrth ewach eraow hicte rfabe yathn utjnd aeaim ontsu reyhe retnp noeqt there ireno lktyn cnssa galat aesis firis awios eegut ahibe onade iyher enoen fthem asiod isfir isawi otell onnoe elsev onyse ather snapu essay allhe idfnr nlkmc aisee knuth ere\n"
      ]
     }
    ],
   },
   {
    "cell_type": "code",
-   "execution_count": 32,
+   "execution_count": 13,
    "metadata": {},
    "outputs": [
     {
   },
   {
    "cell_type": "code",
-   "execution_count": 74,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [
     {
        " 'Z': 'f'}"
       ]
      },
-     "execution_count": 74,
+     "execution_count": 14,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 75,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [
     {
        " 'z': 'X'}"
       ]
      },
-     "execution_count": 75,
+     "execution_count": 15,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 76,
+   "execution_count": 16,
    "metadata": {},
    "outputs": [
     {
   },
   {
    "cell_type": "code",
-   "execution_count": 33,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [
     {
        "3362"
       ]
      },
-     "execution_count": 33,
+     "execution_count": 17,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [],
    "source": [
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [
     {
        "1577"
       ]
      },
-     "execution_count": 4,
+     "execution_count": 19,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 20,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "26"
+       "1577"
       ]
      },
-     "execution_count": 5,
+     "execution_count": 20,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "len(set(sanitise(cb)))"
+    "19 * 83"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 21,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "[('n', 74),\n",
-       " ('i', 72),\n",
-       " ('a', 70),\n",
-       " ('w', 65),\n",
-       " ('e', 64),\n",
-       " ('j', 62),\n",
-       " ('y', 61),\n",
-       " ('m', 55),\n",
-       " ('r', 54),\n",
-       " ('p', 49),\n",
-       " ('v', 49),\n",
-       " ('x', 48),\n",
-       " ('h', 47),\n",
-       " ('g', 41),\n",
-       " ('u', 38),\n",
-       " ('l', 38),\n",
-       " ('k', 35),\n",
-       " ('b', 34),\n",
-       " ('c', 34),\n",
-       " ('t', 33),\n",
-       " ('z', 33),\n",
-       " ('q', 32),\n",
-       " ('s', 29),\n",
-       " ('f', 25),\n",
-       " ('o', 24),\n",
-       " ('d', 22)]"
+       "['  **   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   **  ',\n",
+       " ' *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * ',\n",
+       " '  * *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *  ',\n",
+       " ' *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   * ',\n",
+       " '  *                                                                             *  ',\n",
+       " ' *                                                                               * ',\n",
+       " '  *    *   ***     ***   *   *   *   *    ***  *   * ***   ****  ***   ***  *** *  ',\n",
+       " ' *    * *  *  *   *     * *  **  *  * *    *   ** ** *  *  *     *  *   *    *   * ',\n",
+       " '  *  *   * *   * *     *   * * * * *   *   *   * * * *  *  *     *   *  *    *  *  ',\n",
+       " ' *   ***** ****  *     ***** * * * *****   *   *   * ****  ***   ****   *    *   * ',\n",
+       " '  *  *   * * *   *     *   * * * * *   *   *   *   * *     *     * *    *    *  *  ',\n",
+       " ' *   *   * *  *   *    *   * *  ** *   *   *   *   * *     *     *  *   *    *   * ',\n",
+       " '  *  *   * *   *   *** *   * *   * *   *  ***  *   * *     ****  *   * ***  *** *  ',\n",
+       " ' *                                                                               * ',\n",
+       " '  *                                                                             *  ',\n",
+       " ' * *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   * * ',\n",
+       " '  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  ',\n",
+       " ' *  *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***  * ',\n",
+       " '  *  *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *   *  *  ']"
       ]
      },
-     "execution_count": 6,
+     "execution_count": 21,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "collections.Counter(sanitise(cb)).most_common()"
+    "tt = ''.maketrans({'w': ' ', 'b': '*'})\n",
+    "chunks(bkeyt.translate(tt), 83)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 22,
    "metadata": {},
    "outputs": [],
    "source": [
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[('i', 'n'), ('n', 'o'), ('o', 'o'), ('o', 'a'), ('a', 'd')]"
-      ]
-     },
-     "execution_count": 8,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "[(scb[i-1], scb[i]) for i in range(1, len(scb))][-5:]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 23,
    "metadata": {},
    "outputs": [
     {
        "('fwruffnquevmp', -1750.4576000271347)"
       ]
      },
-     "execution_count": 9,
+     "execution_count": 23,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 24,
    "metadata": {},
    "outputs": [
     {
        "'nieyqmhcenwlqvchdporosweiohtsshreoihpsvrvusdcostmjezkdeytdhrtasupknropbklngeutgnssthjhhnowgbdwvucncdvofhbdltfpvobiuicirayodpwkpoutibtgcenuhpwachchnbrjydhsfvhqszeuvdcpswatvzmxhsiigkobvcagqcdikfstrvkfggbdkvcvstuvufienwiniasbcecmvdwemkwgtnsyjridowjamhuhnrvxssgewikqxpxhashvwvteulpavioctweavlhtikkkximoikdloikrwswfiojrynulrsgtoktknqcporlruoigivdpeddhrntdmrnucnrioiuetpdnftaipjrewjqtercxzvydhiswrtrneonknnfsyrifcdhedoweptjrechehcptdcehirohbcpniloorntrrrtohgeoandszehilxywfqkrgnphglvcojfbetcietpqmfetkalnahiwixdppersmrihrctseahkhtifbokasoozrjgvvkehjsufarunbrtiswzdmriwcrovhdsdeihdteiiustmjupvkhdiisibjloyrdpyxkusdlwfpsehvevdoiudppehzgvcewnhifrvkfggbdkbidptartnotloihpseehrdipeotmlwbcousicucctsktsrlritcanhpdmrehuinioiuuwirsxtanvjshfwyrkacfihfctdipytegvprdowhfldhycrqhfctswdbnagevitctsufsrofoccprycjgnifeikrthnowgcedfedjwiisdtihszeecfvlwjqgehoeicssaqflthagmncpvvifnreneoiioiulytddhuhkrtzsdpevphodehkctsvfhltophksiepsgcohnrnuseajettlhdwhiepjwcenbtbuecihpijncohouidiqjikpovimvsrfridohvchdporhrmhlpnikpacvjiswwgszeyxrcpragclkvxbporlruoigoviferifridicrskiporsyypwgprszenephnicczvttucvqesuiivqleesvfrhcfgchuswrocjslotcnxtiiswkoieatruninitradpaortylcouoahdpehgukcvlrmrafrntaovetrxvyinpenvniquglltisxgy'"
       ]
      },
-     "execution_count": 10,
+     "execution_count": 24,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 25,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "('vejgvvnkgwfol', -1750.4576000271347)"
+       "('arcanaimperii', -1506.8637359274674)"
       ]
      },
-     "execution_count": 11,
+     "execution_count": 25,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 26,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/plain": [
-       "'nieyqmhcenwlqvchdporosweiohtsshreoihpsvrvusdcostmjezkdeytdhrtasupknropbklngeutgnssthjhhnowgbdwvucncdvofhbdltfpvobiuicirayodpwkpoutibtgcenuhpwachchnbrjydhsfvhqszeuvdcpswatvzmxhsiigkobvcagqcdikfstrvkfggbdkvcvstuvufienwiniasbcecmvdwemkwgtnsyjridowjamhuhnrvxssgewikqxpxhashvwvteulpavioctweavlhtikkkximoikdloikrwswfiojrynulrsgtoktknqcporlruoigivdpeddhrntdmrnucnrioiuetpdnftaipjrewjqtercxzvydhiswrtrneonknnfsyrifcdhedoweptjrechehcptdcehirohbcpniloorntrrrtohgeoandszehilxywfqkrgnphglvcojfbetcietpqmfetkalnahiwixdppersmrihrctseahkhtifbokasoozrjgvvkehjsufarunbrtiswzdmriwcrovhdsdeihdteiiustmjupvkhdiisibjloyrdpyxkusdlwfpsehvevdoiudppehzgvcewnhifrvkfggbdkbidptartnotloihpseehrdipeotmlwbcousicucctsktsrlritcanhpdmrehuinioiuuwirsxtanvjshfwyrkacfihfctdipytegvprdowhfldhycrqhfctswdbnagevitctsufsrofoccprycjgnifeikrthnowgcedfedjwiisdtihszeecfvlwjqgehoeicssaqflthagmncpvvifnreneoiioiulytddhuhkrtzsdpevphodehkctsvfhltophksiepsgcohnrnuseajettlhdwhiepjwcenbtbuecihpijncohouidiqjikpovimvsrfridohvchdporhrmhlpnikpacvjiswwgszeyxrcpragclkvxbporlruoigoviferifridicrskiporsyypwgprszenephnicczvttucvqesuiivqleesvfrhcfgchuswrocjslotcnxtiiswkoieatruninitradpaortylcouoahdpehgukcvlrmrafrntaovetrxvyinpenvniquglltisxgy'"
+       "'inhisjournaldatedtheidesofoctoberintheyearoftheconsulshipsofcaeceliustulliuscapitopomponianusplotiusfirmusandgaiuscorneliusgallicanusagricolawrotethemysteryofthebattleatcamulodonumisatlastsolvedcalgacusmaybeabarbariannowbuthewasaromancitizenthenwhobetrayedusallforloveofabarbarianithastakenallmyskillsasaleaderofmentokeephimalivethelegionnairesspendtheireveningsdesigningnewandcruelwaystoexecutehiminrevengefortheshamehebroughtuponusbuthislifeispreciousitistheonlycardlefttoplayinoursearchforsalvationandthereturnofthestolenaquilaeifwecanalsorecoverthecodexthenperhapsitslosscanbeconcealedandourliveswillbesparedreleasingtheromantraitorcalgacusmusthavestuckintheproudagricolasthroatbuthemadeapactwiththeremainingcaledoniiandtravellednorthtoexchangetheprisonerfortheaquilaeandthecodexbutthecunningcaledoniantribesmansetanothertrapandpresentedagricolawithaforgerycunninglyassembledwithpagesfromthebooksstolenwhenthetriberansackedmonsgraupiusfortoolongthesonsofromehadunderestimatedthepeopleinbritanniaandwhiletheaquilaofthelegionhadbeenrestoredbytheexchangetheirhonourwasnotagricolafacedareturntoromehumiliationandalmostcertaindeaththesixthchapterofmytaleofwoeisguardedbylightningbullandoak'"
       ]
      },
-     "execution_count": 12,
+     "execution_count": 26,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 27,
    "metadata": {},
    "outputs": [
     {
        "'a'"
       ]
      },
-     "execution_count": 13,
+     "execution_count": 27,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 28,
    "metadata": {},
    "outputs": [
     {
        "'zyxwvutsrqponmlkjihgfedcba'"
       ]
      },
-     "execution_count": 14,
+     "execution_count": 28,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 29,
    "metadata": {},
    "outputs": [
     {
        "'hveheifhbiicuzbbcfgvvnzafwnkqnndivxopvpdionrsyrmjvjlkaehbrfsmvmtvkqrrftcyspatroqqnbndcyiqretamkaszhcaqidtaxmpfrvenkfimmihgrxnvgqtrmcpzsqzpygininbbstddlcomipnnqgqargdgmrkbijtxnubxpuzjzbizesjbvqmutztdzotjzktjvraioamqznxiwnstbedizjnbjurebqqhldegrzvnynjbsdzprnparcknlqxnmryaisrqjxquzevdcijkzsbmmkczinszckaxrmkvnnzzfwlmksajvrzcqdrkqsdfgvyobqfemifqqrjcoilkydqodhdevxbauvrqdmumxaorieubzrdxvrlcowvodkrsqgzzsizvmdvddrnigrzayrlmednqfhqcblgnvrrntkusntzwdqpoddtvypazkhaqgqneyixiiukmcsfngyktqeftzpdmqtupjzjrkdxsunenxybyvqmqtdmfsdcmjkntbmmplvvvmzwvmzzzzcfymmtfumostdtbnzfkydvmddgrccdbjcnapbmmsrmjvtvztbcmmubemtzmdathxksrckiivczbkqzjvxbbyvqwjzzsinsowitztdzotjzenbyrumpsgbbvxopvgqwrcmfivmjtrjsponmsshdcmdrcmxombkjsopkydzblmhevxbkrcdllmuhranozrmdtidpmfedcbfvwkezzfvgrzxizrwkddefedcmrhtqizqzeqdcmtfcmuagskuoxceehvdbmkvqyiqreszfaqrdnxnmkrmwqgqqkekkieuozbrqmkrnvoizbwizihkukawixdzvbgmevxbtprrablnkvqgdbygzobrrqfzdcmsfnsprfncrxrpvespbsdhsrbvvjrbsbcynefqmilghcpeaqkbygwexspbramjbpmwdvgiatzcveonbzdzbbcfgvcojxcvhvyqusraxdireceehxdkuovylzkilefgvyobqfegiaaqdeeonbfidlyxfgvrhxprefmqgqhiuyiwlivipmasrtbdkfcznxbqcreoocieswonydyhwdtzrsqlmmmunvhwjkbmosmheqovbykgmphjsyprvxkvqwclksryojnnfdqpfgziqoyjpchoeszhetlptcrmllzw'"
       ]
      },
-     "execution_count": 15,
+     "execution_count": 29,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 30,
    "metadata": {},
    "outputs": [
     {
        "('zixzmzrnkvirr', -1506.8637359274674)"
       ]
      },
-     "execution_count": 16,
+     "execution_count": 30,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 31,
    "metadata": {},
    "outputs": [
     {
        "'inhisjournaldatedtheidesofoctoberintheyearoftheconsulshipsofcaeceliustulliuscapitopomponianusplotiusfirmusandgaiuscorneliusgallicanusagricolawrotethemysteryofthebattleatcamulodonumisatlastsolvedcalgacusmaybeabarbariannowbuthewasaromancitizenthenwhobetrayedusallforloveofabarbarianithastakenallmyskillsasaleaderofmentokeephimalivethelegionnairesspendtheireveningsdesigningnewandcruelwaystoexecutehiminrevengefortheshamehebroughtuponusbuthislifeispreciousitistheonlycardlefttoplayinoursearchforsalvationandthereturnofthestolenaquilaeifwecanalsorecoverthecodexthenperhapsitslosscanbeconcealedandourliveswillbesparedreleasingtheromantraitorcalgacusmusthavestuckintheproudagricolasthroatbuthemadeapactwiththeremainingcaledoniiandtravellednorthtoexchangetheprisonerfortheaquilaeandthecodexbutthecunningcaledoniantribesmansetanothertrapandpresentedagricolawithaforgerycunninglyassembledwithpagesfromthebooksstolenwhenthetriberansackedmonsgraupiusfortoolongthesonsofromehadunderestimatedthepeopleinbritanniaandwhiletheaquilaofthelegionhadbeenrestoredbytheexchangetheirhonourwasnotagricolafacedareturntoromehumiliationandalmostcertaindeaththesixthchapterofmytaleofwoeisguardedbylightningbullandoak'"
       ]
      },
-     "execution_count": 17,
+     "execution_count": 31,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 32,
    "metadata": {},
    "outputs": [
     {
        "'arcanaimperii'"
       ]
      },
-     "execution_count": 18,
+     "execution_count": 32,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 100,
+   "execution_count": 33,
    "metadata": {},
    "outputs": [
     {
   },
   {
    "cell_type": "code",
-   "execution_count": 101,
+   "execution_count": 34,
    "metadata": {},
    "outputs": [
     {
        "1465"
       ]
      },
-     "execution_count": 101,
+     "execution_count": 34,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 21,
+   "execution_count": 35,
    "metadata": {},
    "outputs": [],
    "source": [
-    "def b2_encipher(message, keyword):\n",
-    "    \"\"\"Vigenere encipher\n",
+    "def beaufort_encipher(message, keyword):\n",
+    "    \"\"\"Beaufort encipher\n",
     "\n",
-    "    >>> vigenere_encipher('hello', 'abc')\n",
-    "    'hfnlp'\n",
+    "    >>> beaufort_encipher('inhisjournaldatedtheidesofoctober', 'arcanaimperii')\n",
+    "    'sevsvrusyrrxfayyxuteemazudmpjmmwr'\n",
     "    \"\"\"\n",
     "    shifts = [pos(l) for l in sanitise(keyword)]\n",
     "    pairs = zip(message, cycle(shifts))\n",
   },
   {
    "cell_type": "code",
-   "execution_count": 22,
+   "execution_count": 36,
    "metadata": {},
    "outputs": [
     {
        "'sevsvrusyrrxfayyxuteemazudmpjmmwr'"
       ]
      },
-     "execution_count": 22,
+     "execution_count": 36,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "b2_encipher('inhisjournaldatedtheidesofoctober', 'arcanaimperii')"
+    "beaufort_encipher('inhisjournaldatedtheidesofoctober', 'arcanaimperii')"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 25,
+   "execution_count": 37,
    "metadata": {},
    "outputs": [],
    "source": [
-    "b2_decipher = b2_encipher"
+    "beaufort_decipher = beaufort_encipher"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 26,
+   "execution_count": 38,
    "metadata": {},
    "outputs": [
     {
        "'inhisjournaldatedtheidesofoctober'"
       ]
      },
-     "execution_count": 26,
+     "execution_count": 38,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "beaufort_decipher('sevsvrusyrrxfayyxuteemazudmpjmmwr', 'arcanaimperii')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 39,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# def beaufort_sub_break(message, fitness=Pletters):\n",
+    "#     best_shift = 0\n",
+    "#     best_fit = float('-inf')\n",
+    "#     for key in range(26):\n",
+    "#         plaintext = [unpos(key - pos(l)) for l in message]\n",
+    "#         fit = fitness(plaintext)\n",
+    "#         logger.debug('Beaufort sub break attempt using key {0} gives fit of {1} '\n",
+    "#                      'and decrypt starting: {2}'.format(key, fit,\n",
+    "#                                                         plaintext[:50]))\n",
+    "#         if fit > best_fit:\n",
+    "#             best_fit = fit\n",
+    "#             best_key = key\n",
+    "#     logger.info('Beaufort sub break best fit: key {0} gives fit of {1} and '\n",
+    "#                 'decrypt starting: {2}'.format(best_key, best_fit, \n",
+    "#                     cat([unpos(best_key - pos(l)) for l in message[:50]])))\n",
+    "#     return best_key, best_fit"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 40,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# def beaufort_frequency_break(message, max_key_length=20, fitness=Pletters):\n",
+    "#     \"\"\"Breaks a Beaufort cipher with frequency analysis\n",
+    "\n",
+    "#     >>> beaufort_frequency_break(beaufort_encipher(sanitise(\"It is time to \" \\\n",
+    "#             \"run. She is ready and so am I. I stole Daniel's pocketbook this \" \\\n",
+    "#             \"afternoon when he left his jacket hanging on the easel in the \" \\\n",
+    "#             \"attic. I jump every time I hear a footstep on the stairs, \" \\\n",
+    "#             \"certain that the theft has been discovered and that I will \" \\\n",
+    "#             \"be caught. The SS officer visits less often now \" \\\n",
+    "#             \"that he is sure\"), 'florence')) # doctest: +ELLIPSIS\n",
+    "#     ('florence', -307.5473096791...)\n",
+    "#     \"\"\"\n",
+    "#     def worker(message, key_length, fitness):\n",
+    "#         splits = every_nth(message, key_length)\n",
+    "#         key = cat([unpos(beaufort_sub_break(s)[0]) for s in splits])\n",
+    "#         plaintext = beaufort_decipher(message, key)\n",
+    "#         fit = fitness(plaintext)\n",
+    "#         return key, fit\n",
+    "#     sanitised_message = sanitise(message)\n",
+    "#     results = starmap(worker, [(sanitised_message, i, fitness)\n",
+    "#                                for i in range(1, max_key_length+1)])\n",
+    "#     return max(results, key=lambda k: k[1])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 42,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "('arcanaimperii', -1506.8637359274674)"
+      ]
+     },
+     "execution_count": 42,
      "metadata": {},
      "output_type": "execute_result"
     }
    ],
    "source": [
-    "b2_decipher('sevsvrusyrrxfayyxuteemazudmpjmmwr', 'arcanaimperii')"
+    "kb, sb = beaufort_frequency_break(cb)\n",
+    "kb, sb"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 43,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "In his Journal dated the Ides of October in the year of the consulships of Caecelius Tullius Capito\n",
+      "Pomponianus Plotius Firmus and Gaius Cornelius Gallicanus, Agricola wrote “The mystery of the battle\n",
+      "at Camulodonum is at last solved. Calgacus may be a Barbarian now, but he was a Roman citizen then,\n",
+      "who betrayed us all for love of a barbarian. It has taken all my skills as a leader of men to keep\n",
+      "him alive. The Legionnaires spend their evenings designing new and cruel ways to execute him in\n",
+      "revenge for the shame he brought upon us, but his life is precious. It is the only card left to play\n",
+      "in our search for salvation and the return of the stolen Aquilae. If we can also recover the Codex\n",
+      "then perhaps its loss can be concealed and our lives will be spared.” Releasing the Roman traitor\n",
+      "Calgacus must have stuck in the proud Agricola’s throat, but he made a pact with the remaining\n",
+      "Caledonii and travelled north to exchange the prisoner for the Aquilae and the Codex. But the\n",
+      "cunning Caledonian tribesman set another trap and presented Agricola with a forgery cunningly\n",
+      "assembled with pages from the books stolen when the tribe ransacked Mons Graupius. For too long the\n",
+      "sons of Rome had underestimated the people in Britannia and while the Aquila of the Legion had been\n",
+      "restored by the exchange, their honour was not. Agricola faced a return to Rome, humiliation and\n",
+      "almost certain death. The sixth chapter of my tale of woe is guarded by lightning, bull and oak.\n"
+     ]
+    }
+   ],
+   "source": [
+    "pub = depunctuate(cb)\n",
+    "pb = beaufort_decipher(scb, kb)\n",
+    "print(lcat(tpack(repunctuate(pb, pub).split())))"
    ]
   },
   {