Merge branch 'master' of git.njae.me.uk:cas-master-teacher-training
[cas-master-teacher-training.git] / hangman-better.ipynb
index 7691ed14b7530fbf1cf00f6c45c88c0b9fbd6f6d..20a12e03e4eb6ebebb04a351612e4093ee64f41c 100644 (file)
@@ -1,7 +1,7 @@
 {
  "metadata": {
   "name": "",
-  "signature": "sha256:07f366ec645d178071697c25c43d438fd80cf3a676ad9bd8d7613f5bfa36adf5"
+  "signature": "sha256:32b3b300745f022158bfde0b3fc0a50b11d36551211371d50140e9f9d2a7b8e1"
  },
  "nbformat": 3,
  "nbformat_minor": 0,
       "        self.game_lost = False\n",
       "    \n",
       "    def find_all(self, letter):\n",
-      "        locations = []\n",
-      "        starting=0\n",
-      "        location = self.target.find(letter)\n",
-      "        while location > -1:\n",
-      "            locations += [location]\n",
-      "            starting = location + 1\n",
-      "            location = self.target.find(letter, starting)\n",
-      "        return locations\n",
+      "        return [p for p, l in enumerate(self.target) if l == letter]\n",
       "    \n",
       "    def update_discovered_word(self, guessed_letter):\n",
       "        locations = self.find_all(guessed_letter)\n",
       "        \n",
       "    def guess(self, discovered, missed, lives):\n",
       "        guessed_letters = [l.lower() for l in discovered + missed if l in string.ascii_letters]\n",
-      "        self.ordered_subtract(guessed_letters)\n",
-      "        return self.ordered_letters[0]\n",
+      "        return [l for l in self.ordered_letters if l not in guessed_letters][0]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 6
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class PlayerAlphabetical(PlayerFixedOrder):\n",
+      "    def __init__(self):\n",
+      "        super().__init__(string.ascii_lowercase)\n",
+      "\n",
+      "class PlayerFreqOrdered(PlayerFixedOrder):\n",
+      "    def __init__(self):\n",
+      "        super().__init__(LETTERS_IN_ORDER)\n"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 7
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%%timeit\n",
+      "\n",
+      "wins = 0\n",
+      "for _ in range(1000):\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAlphabetical())\n",
+      "    g.play_game()\n",
+      "    if g.game_won:\n",
+      "        wins += 1\n",
+      "print(wins)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "50\n",
+        "59"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "33"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "53"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "49"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "42"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "43"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "54"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "49"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "54"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "45"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "46"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "44"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "45"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "48"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "51"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "46"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "54"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "49"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "44"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "42"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "44"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "48"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "39"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "46"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "43"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "50"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "47"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "52"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "46"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "55"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "47"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "39"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "55"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "40"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "46"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "53"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "43"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "40"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "53"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "41"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "10 loops, best of 3: 64.2 ms per loop\n"
+       ]
+      }
+     ],
+     "prompt_number": 54
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%%timeit\n",
+      "\n",
+      "wins = 0\n",
+      "for _ in range(1000):\n",
+      "    g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
+      "    g.play_game()\n",
+      "    if g.game_won:\n",
+      "        wins += 1\n",
+      "print(wins)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "334\n",
+        "342"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "318"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "313"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "353"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "304"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "332"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "313"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "335"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "339"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "328"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "334"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "322"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "347"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "334"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "340"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "319"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "365"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "315"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "307"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "314"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "317"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "310"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "324"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "313"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "318"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "314"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "324"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "297"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "335"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "335"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "343"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "342"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "318"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "306"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "353"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "332"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "330"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "334"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "307"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "306"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "10 loops, best of 3: 96.2 ms per loop\n"
+       ]
+      }
+     ],
+     "prompt_number": 56
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%%timeit\n",
       "\n",
-      "    def ordered_subtract(self, to_remove):\n",
-      "        for r in to_remove:\n",
-      "            if r in self.ordered_letters:\n",
-      "                ri = self.ordered_letters.index(r)\n",
-      "                self.ordered_letters = self.ordered_letters[:ri] + self.ordered_letters[ri+1:]"
+      "wins = 0\n",
+      "for _ in range(1000):\n",
+      "    g = Game(random.choice(WORDS), player=PlayerFixedOrder(list(reversed(string.ascii_lowercase))))\n",
+      "    g.play_game()\n",
+      "    if g.game_won:\n",
+      "        wins += 1\n",
+      "print(wins)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "7\n",
+        "5"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "6"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "7"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "7"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "10"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "8"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "5"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "5"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "5"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "9"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "3"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "8"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "13"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "8"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "10"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "9"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "9"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "12"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "6"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "6"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "14"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "9"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "8"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "8"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "7"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "9"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "10"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "6"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "7"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "6"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "4"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "6"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "3"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "7"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "7"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "10"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "11"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "6"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "4"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "10 loops, best of 3: 74.6 ms per loop\n"
+       ]
+      }
      ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [],
-     "prompt_number": 6
+     "prompt_number": 57
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "class PlayerAlphabetical(PlayerFixedOrder):\n",
-      "    def __init__(self):\n",
-      "        super().__init__(string.ascii_lowercase)\n",
-      "\n",
-      "class PlayerFreqOrdered(PlayerFixedOrder):\n",
-      "    def __init__(self):\n",
-      "        super().__init__(LETTERS_IN_ORDER)\n"
+      "DICT_COUNTS = collections.Counter(l.lower() for l in open('/usr/share/dict/british-english').read() if l in string.ascii_letters)\n",
+      "DICT_LETTERS_IN_ORDER = [p[0] for p in DICT_COUNTS.most_common()]"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 7
+     "prompt_number": 11
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "wins = 0\n",
-      "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAlphabetical())\n",
-      "    g.play_game()\n",
-      "    if g.game_won:\n",
-      "        wins += 1\n",
-      "print(wins)"
+      "DICT_COUNTS"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [
       {
-       "output_type": "stream",
-       "stream": "stdout",
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 12,
        "text": [
-        "43\n"
+        "Counter({'s': 91332, 'e': 88692, 'i': 66900, 'a': 64468, 'r': 57460, 'n': 57128, 't': 52949, 'o': 49121, 'l': 40995, 'c': 31854, 'd': 28505, 'u': 26372, 'g': 22693, 'm': 22549, 'p': 22249, 'h': 19337, 'b': 15540, 'y': 12652, 'f': 10679, 'k': 8386, 'v': 8000, 'w': 7505, 'x': 2125, 'z': 2058, 'j': 1950, 'q': 1536})"
        ]
       }
      ],
-     "prompt_number": 8
+     "prompt_number": 12
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "wins = 0\n",
-      "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
-      "    g.play_game()\n",
-      "    if g.game_won:\n",
-      "        wins += 1\n",
-      "print(wins)"
+      "print(DICT_LETTERS_IN_ORDER)\n",
+      "print(LETTERS_IN_ORDER)"
      ],
      "language": "python",
      "metadata": {},
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "316\n"
+        "['s', 'e', 'i', 'a', 'r', 'n', 't', 'o', 'l', 'c', 'd', 'u', 'g', 'm', 'p', 'h', 'b', 'y', 'f', 'k', 'v', 'w', 'x', 'z', 'j', 'q']\n",
+        "['e', 't', 'a', 'o', 'i', 'h', 'n', 's', 'r', 'd', 'l', 'u', 'm', 'w', 'c', 'y', 'f', 'g', 'p', 'b', 'v', 'k', 'x', 'j', 'q', 'z']\n"
        ]
       }
      ],
-     "prompt_number": 9
+     "prompt_number": 13
     },
     {
      "cell_type": "code",
      "input": [
       "wins = 0\n",
       "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerFixedOrder(list(reversed(string.ascii_lowercase))))\n",
+      "    g = Game(random.choice(WORDS), player=PlayerFixedOrder(DICT_LETTERS_IN_ORDER))\n",
       "    g.play_game()\n",
       "    if g.game_won:\n",
       "        wins += 1\n",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "5\n"
+        "440\n"
        ]
       }
      ],
-     "prompt_number": 10
+     "prompt_number": 14
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "DICT_COUNTS = collections.Counter(l.lower() for l in open('/usr/share/dict/british-english').read() if l in string.ascii_letters)\n",
-      "DICT_LETTERS_IN_ORDER = [p[0] for p in DICT_COUNTS.most_common()]"
+      "class PlayerAdaptiveLength:\n",
+      "    def __init__(self, words):\n",
+      "        self.all_words = words\n",
+      "        self.candidate_words = None\n",
+      "        \n",
+      "    def guess(self, discovered, missed, lives):\n",
+      "        if not self.candidate_words:\n",
+      "            self.set_ordered_letters(len(discovered))\n",
+      "        guessed_letters = [l.lower() for l in discovered + missed if l in string.ascii_letters]\n",
+      "        return [l for l in self.ordered_letters if l not in guessed_letters][0]\n",
+      "    \n",
+      "    def set_ordered_letters(self, word_len):\n",
+      "        self.candidate_words = [w for w in self.all_words if len(w) == word_len]\n",
+      "        counts = collections.Counter(l.lower() for l in ''.join(self.candidate_words) if l in string.ascii_letters)\n",
+      "        self.ordered_letters = [p[0] for p in counts.most_common()]"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 11
+     "prompt_number": 33
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "DICT_COUNTS"
+      "wins = 0\n",
+      "for _ in range(1000):\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveLength(WORDS))\n",
+      "    g.play_game()\n",
+      "    if g.game_won:\n",
+      "        wins += 1\n",
+      "print(wins)"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [
       {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 12,
+       "output_type": "stream",
+       "stream": "stdout",
        "text": [
-        "Counter({'s': 91332, 'e': 88692, 'i': 66900, 'a': 64468, 'r': 57460, 'n': 57128, 't': 52949, 'o': 49121, 'l': 40995, 'c': 31854, 'd': 28505, 'u': 26372, 'g': 22693, 'm': 22549, 'p': 22249, 'h': 19337, 'b': 15540, 'y': 12652, 'f': 10679, 'k': 8386, 'v': 8000, 'w': 7505, 'x': 2125, 'z': 2058, 'j': 1950, 'q': 1536})"
+        "474\n"
        ]
       }
      ],
-     "prompt_number": 12
+     "prompt_number": 34
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "print(DICT_LETTERS_IN_ORDER)\n",
-      "print(LETTERS_IN_ORDER)"
+      "class PlayerAdaptiveIncludedLetters:\n",
+      "    def __init__(self, words):\n",
+      "        self.candidate_words = words\n",
+      "        \n",
+      "    def guess(self, discovered, missed, lives):\n",
+      "        self.filter_candidate_words(discovered)\n",
+      "        self.set_ordered_letters()\n",
+      "        guessed_letters = [l.lower() for l in discovered + missed if l in string.ascii_letters]\n",
+      "        return [l for l in self.ordered_letters if l not in guessed_letters][0]\n",
+      "    \n",
+      "    def filter_candidate_words(self, discovered):\n",
+      "        exp = re.compile('^' + ''.join(discovered).replace('_', '.') + '$')\n",
+      "        self.candidate_words = [w for w in self.candidate_words if exp.match(w)]\n",
+      "        \n",
+      "    def set_ordered_letters(self):\n",
+      "        counts = collections.Counter(l.lower() for l in ''.join(self.candidate_words) if l in string.ascii_letters)\n",
+      "        self.ordered_letters = [p[0] for p in counts.most_common()]"
      ],
      "language": "python",
      "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "['s', 'e', 'i', 'a', 'r', 'n', 't', 'o', 'l', 'c', 'd', 'u', 'g', 'm', 'p', 'h', 'b', 'y', 'f', 'k', 'v', 'w', 'x', 'z', 'j', 'q']\n",
-        "['e', 't', 'a', 'o', 'i', 'h', 'n', 's', 'r', 'd', 'l', 'u', 'm', 'w', 'c', 'y', 'f', 'g', 'p', 'b', 'v', 'k', 'x', 'j', 'q', 'z']\n"
-       ]
-      }
-     ],
-     "prompt_number": 13
+     "outputs": [],
+     "prompt_number": 35
     },
     {
      "cell_type": "code",
      "input": [
       "wins = 0\n",
       "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerFixedOrder(DICT_LETTERS_IN_ORDER))\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveIncludedLetters(WORDS))\n",
       "    g.play_game()\n",
       "    if g.game_won:\n",
       "        wins += 1\n",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "473\n"
+        "982\n"
        ]
       }
      ],
-     "prompt_number": 14
+     "prompt_number": 36
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "class PlayerAdaptiveLength:\n",
+      "re.match('^[^xaz]*$', 'happy')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 37
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class PlayerAdaptiveExcludedLetters:\n",
       "    def __init__(self, words):\n",
-      "        self.all_words = words\n",
-      "        self.candidate_words = None\n",
+      "        self.candidate_words = words\n",
       "        \n",
       "    def guess(self, discovered, missed, lives):\n",
-      "        if not self.candidate_words:\n",
-      "            self.set_ordered_letters(len(discovered))\n",
+      "        self.filter_candidate_words(missed)\n",
+      "        self.set_ordered_letters()\n",
       "        guessed_letters = [l.lower() for l in discovered + missed if l in string.ascii_letters]\n",
-      "        self.ordered_subtract(guessed_letters)\n",
-      "        return self.ordered_letters[0]\n",
-      "\n",
-      "    def ordered_subtract(self, to_remove):\n",
-      "        for r in to_remove:\n",
-      "            if r in self.ordered_letters:\n",
-      "                ri = self.ordered_letters.index(r)\n",
-      "                self.ordered_letters = self.ordered_letters[:ri] + self.ordered_letters[ri+1:]\n",
+      "        return [l for l in self.ordered_letters if l not in guessed_letters][0]\n",
       "    \n",
-      "    def set_ordered_letters(self, word_len):\n",
-      "        self.candidate_words = [w for w in self.all_words if len(w) == word_len]\n",
+      "    def filter_candidate_words(self, missed):\n",
+      "        if missed:\n",
+      "            exp = re.compile('^[^' + ''.join(missed) + ']*$')\n",
+      "            self.candidate_words = [w for w in self.candidate_words if exp.match(w)]\n",
+      "        \n",
+      "    def set_ordered_letters(self):\n",
       "        counts = collections.Counter(l.lower() for l in ''.join(self.candidate_words) if l in string.ascii_letters)\n",
       "        self.ordered_letters = [p[0] for p in counts.most_common()]"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 15
+     "prompt_number": 38
     },
     {
      "cell_type": "code",
      "input": [
       "wins = 0\n",
       "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveLength(WORDS))\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveExcludedLetters(WORDS))\n",
       "    g.play_game()\n",
       "    if g.game_won:\n",
       "        wins += 1\n",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "489\n"
+        "502\n"
        ]
       }
      ],
-     "prompt_number": 16
+     "prompt_number": 39
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "class PlayerAdaptiveIncludedLetters:\n",
+      "class PlayerAdaptivePattern:\n",
       "    def __init__(self, words):\n",
       "        self.candidate_words = words\n",
       "        \n",
       "    def guess(self, discovered, missed, lives):\n",
-      "        self.filter_candidate_words(discovered)\n",
+      "        self.filter_candidate_words(discovered, missed)\n",
       "        self.set_ordered_letters()\n",
       "        guessed_letters = [l.lower() for l in discovered + missed if l in string.ascii_letters]\n",
-      "        self.ordered_subtract(guessed_letters)\n",
-      "        return self.ordered_letters[0]\n",
-      "\n",
-      "    def ordered_subtract(self, to_remove):\n",
-      "        for r in to_remove:\n",
-      "            if r in self.ordered_letters:\n",
-      "                ri = self.ordered_letters.index(r)\n",
-      "                self.ordered_letters = self.ordered_letters[:ri] + self.ordered_letters[ri+1:]\n",
+      "        return [l for l in self.ordered_letters if l not in guessed_letters][0]\n",
       "    \n",
-      "    def filter_candidate_words(self, discovered):\n",
-      "        exp = re.compile('^' + ''.join(discovered).replace('_', '.') + '$')\n",
+      "    def filter_candidate_words(self, discovered, missed):\n",
+      "        attempted_letters = list(set(l.lower() for l in discovered + missed if l in string.ascii_letters))\n",
+      "        if attempted_letters:\n",
+      "            exclusion_pattern = '[^' + ''.join(attempted_letters) + ']'\n",
+      "        else:\n",
+      "            exclusion_pattern = '.'\n",
+      "        exp = re.compile('^' + ''.join(discovered).replace('_', exclusion_pattern) + '$')\n",
       "        self.candidate_words = [w for w in self.candidate_words if exp.match(w)]\n",
       "        \n",
       "    def set_ordered_letters(self):\n",
       "        counts = collections.Counter(l.lower() for l in ''.join(self.candidate_words) if l in string.ascii_letters)\n",
-      "        self.ordered_letters = [p[0] for p in counts.most_common()]"
+      "        self.ordered_letters = [p[0] for p in counts.most_common()]\n"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 17
+     "prompt_number": 40
     },
     {
      "cell_type": "code",
      "input": [
       "wins = 0\n",
       "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveIncludedLetters(WORDS))\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptivePattern(WORDS))\n",
       "    g.play_game()\n",
       "    if g.game_won:\n",
       "        wins += 1\n",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "979\n"
+        "993\n"
        ]
       }
      ],
-     "prompt_number": 18
+     "prompt_number": 41
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "re.match('^[^xaz]*$', 'happy')"
+      "%%timeit\n",
+      "\n",
+      "wins = 0\n",
+      "for _ in range(1000):\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptivePattern(WORDS))\n",
+      "    g.play_game()\n",
+      "    if g.game_won:\n",
+      "        wins += 1\n",
+      "print(wins)"
      ],
      "language": "python",
      "metadata": {},
-     "outputs": [],
-     "prompt_number": 19
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "994\n",
+        "993"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "987"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "993"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 30.6 s per loop\n"
+       ]
+      }
+     ],
+     "prompt_number": 42
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "class PlayerAdaptiveExcludedLetters:\n",
+      "class PlayerAdaptive:\n",
       "    def __init__(self, words):\n",
       "        self.candidate_words = words\n",
       "        \n",
       "    def guess(self, discovered, missed, lives):\n",
-      "        self.filter_candidate_words(missed)\n",
+      "        self.filter_candidate_words(discovered, missed)\n",
       "        self.set_ordered_letters()\n",
       "        guessed_letters = [l.lower() for l in discovered + missed if l in string.ascii_letters]\n",
-      "        self.ordered_subtract(guessed_letters)\n",
-      "        return self.ordered_letters[0]\n",
-      "\n",
-      "    def ordered_subtract(self, to_remove):\n",
-      "        for r in to_remove:\n",
-      "            if r in self.ordered_letters:\n",
-      "                ri = self.ordered_letters.index(r)\n",
-      "                self.ordered_letters = self.ordered_letters[:ri] + self.ordered_letters[ri+1:]\n",
+      "        return [l for l in self.ordered_letters if l not in guessed_letters][0]\n",
       "    \n",
-      "    def filter_candidate_words(self, missed):\n",
-      "        if missed:\n",
-      "            exp = re.compile('^[^' + ''.join(missed) + ']*$')\n",
-      "            self.candidate_words = [w for w in self.candidate_words if exp.match(w)]\n",
+      "    def filter_candidate_words(self, discovered, missed):\n",
+      "        pass\n",
       "        \n",
       "    def set_ordered_letters(self):\n",
-      "        counts = collections.Counter(l.lower() for l in ''.join(self.candidate_words) if l in string.ascii_letters)\n",
+      "        counts = collections.Counter(l.lower() \n",
+      "                                     for l in ''.join(self.candidate_words) + string.ascii_lowercase \n",
+      "                                     if l in string.ascii_letters)\n",
       "        self.ordered_letters = [p[0] for p in counts.most_common()]"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 20
+     "prompt_number": 43
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class PlayerAdaptiveLength(PlayerAdaptive):\n",
+      "    def __init__(self, words):\n",
+      "        super().__init__(words)\n",
+      "        self.word_len = None\n",
+      "        self.ordered_letters = None\n",
+      "        \n",
+      "    def filter_candidate_words(self, discovered, missed):\n",
+      "        if not self.word_len:\n",
+      "            self.word_len = len(discovered)\n",
+      "            self.candidate_words = [w for w in self.candidate_words if len(w) == self.word_len]\n",
+      "    \n",
+      "    def set_ordered_letters(self):\n",
+      "        if not self.ordered_letters:\n",
+      "            super().set_ordered_letters()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 44
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class PlayerAdaptiveIncludedLetters(PlayerAdaptive):\n",
+      "    def filter_candidate_words(self, discovered, missed):\n",
+      "        exp = re.compile('^' + ''.join(discovered).replace('_', '.') + '$')\n",
+      "        self.candidate_words = [w for w in self.candidate_words if exp.match(w)]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 45
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class PlayerAdaptiveExcludedLetters(PlayerAdaptive):\n",
+      "    def filter_candidate_words(self, discovered, missed):\n",
+      "        if missed:\n",
+      "            exp = re.compile('^[^' + ''.join(missed) + ']*$')\n",
+      "            self.candidate_words = [w for w in self.candidate_words if exp.match(w)]        "
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 46
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class PlayerAdaptivePattern(PlayerAdaptive):\n",
+      "    def filter_candidate_words(self, discovered, missed):\n",
+      "        attempted_letters = [l for l in discovered if l != '_'] + missed\n",
+      "        if attempted_letters:\n",
+      "            exclusion_pattern = '[^' + ''.join(attempted_letters) + ']'\n",
+      "        else:\n",
+      "            exclusion_pattern = '.'\n",
+      "        exp = re.compile('^' + ''.join(discovered).replace('_', exclusion_pattern) + '$')\n",
+      "        self.candidate_words = [w for w in self.candidate_words if exp.match(w)]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 47
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
+      "%%timeit\n",
+      "\n",
       "wins = 0\n",
       "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveExcludedLetters(WORDS))\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveLength(WORDS))\n",
       "    g.play_game()\n",
       "    if g.game_won:\n",
       "        wins += 1\n",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "512\n"
+        "479\n",
+        "455"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "460"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "498"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 15.9 s per loop\n"
        ]
       }
      ],
-     "prompt_number": 21
+     "prompt_number": 48
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "class PlayerAdaptivePattern:\n",
-      "    def __init__(self, words):\n",
-      "        self.candidate_words = words\n",
-      "        \n",
-      "    def guess(self, discovered, missed, lives):\n",
-      "        self.filter_candidate_words(discovered, missed)\n",
-      "        self.set_ordered_letters()\n",
-      "        guessed_letters = [l.lower() for l in discovered + missed if l in string.ascii_letters]\n",
-      "        self.ordered_subtract(guessed_letters)\n",
-      "        return self.ordered_letters[0]\n",
+      "%%timeit\n",
       "\n",
-      "    def ordered_subtract(self, to_remove):\n",
-      "        for r in to_remove:\n",
-      "            if r in self.ordered_letters:\n",
-      "                ri = self.ordered_letters.index(r)\n",
-      "                self.ordered_letters = self.ordered_letters[:ri] + self.ordered_letters[ri+1:]\n",
-      "    \n",
-      "    def filter_candidate_words(self, discovered, missed):\n",
-      "        attempted_letters = list(set(l.lower() for l in discovered + missed if l in string.ascii_letters))\n",
-      "        if attempted_letters:\n",
-      "            exclusion_pattern = '[^' + ''.join(attempted_letters) + ']'\n",
-      "        else:\n",
-      "            exclusion_pattern = '.'\n",
-      "        exp = re.compile('^' + ''.join(discovered).replace('_', exclusion_pattern) + '$')\n",
-      "        self.candidate_words = [w for w in self.candidate_words if exp.match(w)]\n",
-      "        \n",
-      "    def set_ordered_letters(self):\n",
-      "        counts = collections.Counter(l.lower() for l in ''.join(self.candidate_words) if l in string.ascii_letters)\n",
-      "        self.ordered_letters = [p[0] for p in counts.most_common()]\n"
+      "wins = 0\n",
+      "for _ in range(1000):\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveIncludedLetters(WORDS))\n",
+      "    g.play_game()\n",
+      "    if g.game_won:\n",
+      "        wins += 1\n",
+      "print(wins)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "981\n",
+        "983"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "980"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "980"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 36.9 s per loop\n"
+       ]
+      }
      ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [],
-     "prompt_number": 22
+     "prompt_number": 49
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
+      "%%timeit\n",
+      "\n",
       "wins = 0\n",
       "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAdaptivePattern(WORDS))\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveExcludedLetters(WORDS))\n",
       "    g.play_game()\n",
       "    if g.game_won:\n",
       "        wins += 1\n",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "990\n"
+        "521\n",
+        "484"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "491"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "518"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 7min 18s per loop\n"
        ]
       }
      ],
-     "prompt_number": 23
+     "prompt_number": 50
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "986\n",
-        "996"
+        "987\n",
+        "988"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "991"
+        "996"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "990"
+        "995"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "1 loops, best of 3: 57.2 s per loop\n"
+        "1 loops, best of 3: 31.1 s per loop\n"
        ]
       }
      ],
-     "prompt_number": 24
+     "prompt_number": 51
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "class PlayerAdaptive:\n",
+      "for _ in range(1000):\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptivePattern(WORDS))\n",
+      "    g.play_game()\n",
+      "    if not g.game_won:\n",
+      "        print(g.target, g.discovered, g.wrong_letters)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "naked ['_', 'a', '_', 'e', 'd'] ['s', 'r', 'w', 'c', 't', 'g', 'l', 'f', 'p', 'm']\n",
+        "wound"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'o', 'u', 'n', 'd'] ['s', 'e', 'a', 'y', 'p', 'm', 'f', 'h', 'b', 'r']\n",
+        "hut"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'u', 't'] ['a', 'o', 'e', 'i', 'b', 'g', 'n', 'p', 'm', 'c']\n",
+        "fucker"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'u', '_', '_', 'e', 'r'] ['d', 's', 'i', 'a', 'o', 't', 'b', 'n', 'm', 'l']\n",
+        "fox"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'o', '_'] ['a', 't', 'b', 'd', 'w', 'p', 'n', 's', 'g', 'y']\n",
+        "wills"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'i', 'l', 'l', 's'] ['e', 'o', 'a', 'g', 'p', 'm', 'k', 'f', 'h', 'd']\n",
+        "bunny"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'u', 'n', 'n', 'y'] ['s', 'e', 'a', 'o', 'i', 'm', 'l', 'g', 'f', 't']\n",
+        "curving"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['c', 'u', '_', '_', 'i', 'n', 'g'] ['e', 'a', 'o', 'l', 's', 'f', 'p', 'b', 't', 'm']\n",
+        "hoe"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'o', '_'] ['a', 't', 'b', 'd', 'w', 'p', 'n', 's', 'g', 'y']\n",
+        "butt"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'u', '_', '_'] ['e', 's', 'o', 'a', 'i', 'l', 'f', 'r', 'n', 'm']\n",
+        "mucked"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'u', '_', '_', 'e', 'd'] ['a', 'o', 'i', 'l', 's', 't', 'f', 'p', 'g', 'b']\n",
+        "flaw"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['f', 'l', 'a', '_'] ['e', 's', 'o', 'r', 'y', 'g', 'k', 'x', 'p', 'n']\n"
+       ]
+      }
+     ],
+     "prompt_number": 52
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "iterations = 10000\n",
+      "wins = 0\n",
+      "for _ in range(iterations):\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptivePattern(WORDS))\n",
+      "    g.play_game()\n",
+      "    if g.game_won:\n",
+      "        wins += 1\n",
+      "print(wins / iterations)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "0.9936\n"
+       ]
+      }
+     ],
+     "prompt_number": 53
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class PlayerAdaptiveNoRegex:\n",
       "    def __init__(self, words):\n",
       "        self.candidate_words = words\n",
       "        \n",
       "        self.filter_candidate_words(discovered, missed)\n",
       "        self.set_ordered_letters()\n",
       "        guessed_letters = [l.lower() for l in discovered + missed if l in string.ascii_letters]\n",
-      "        self.ordered_subtract(guessed_letters)\n",
-      "        return self.ordered_letters[0]\n",
-      "\n",
-      "    def ordered_subtract(self, to_remove):\n",
-      "        for r in to_remove:\n",
-      "            if r in self.ordered_letters:\n",
-      "                ri = self.ordered_letters.index(r)\n",
-      "                self.ordered_letters = self.ordered_letters[:ri] + self.ordered_letters[ri+1:]\n",
+      "        return [l for l in self.ordered_letters if l not in guessed_letters][0]\n",
       "    \n",
       "    def filter_candidate_words(self, discovered, missed):\n",
       "        pass\n",
       "        counts = collections.Counter(l.lower() \n",
       "                                     for l in ''.join(self.candidate_words) + string.ascii_lowercase \n",
       "                                     if l in string.ascii_letters)\n",
-      "        self.ordered_letters = [p[0] for p in counts.most_common()]"
+      "        self.ordered_letters = [p[0] for p in counts.most_common()]\n",
+      "\n",
+      "    def match(self, pattern, target, excluded=None):\n",
+      "        if not excluded:\n",
+      "            excluded = ''\n",
+      "        if len(pattern) != len(target):\n",
+      "            return False\n",
+      "        for m, c in zip(pattern, target):\n",
+      "            if m == '_' and c not in excluded:\n",
+      "                # true\n",
+      "                pass\n",
+      "            elif m != '_' and m == c:\n",
+      "                # true\n",
+      "                pass\n",
+      "            else:\n",
+      "                return False\n",
+      "        return True        "
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 25
+     "prompt_number": 59
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "class PlayerAdaptiveLength(PlayerAdaptive):\n",
+      "class PlayerAdaptiveLengthNoRegex(PlayerAdaptiveNoRegex):\n",
       "    def __init__(self, words):\n",
       "        super().__init__(words)\n",
       "        self.word_len = None\n",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 26
+     "prompt_number": 60
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "class PlayerAdaptiveIncludedLetters(PlayerAdaptive):\n",
+      "class PlayerAdaptiveIncludedLettersNoRegex(PlayerAdaptiveNoRegex):\n",
       "    def filter_candidate_words(self, discovered, missed):\n",
-      "        exp = re.compile('^' + ''.join(discovered).replace('_', '.') + '$')\n",
-      "        self.candidate_words = [w for w in self.candidate_words if exp.match(w)]"
+      "        self.candidate_words = [w for w in self.candidate_words if self.match(discovered, w)]"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 27
+     "prompt_number": 61
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "class PlayerAdaptiveExcludedLetters(PlayerAdaptive):\n",
+      "class PlayerAdaptiveExcludedLettersNoRegex(PlayerAdaptiveNoRegex):\n",
       "    def filter_candidate_words(self, discovered, missed):\n",
       "        if missed:\n",
-      "            exp = re.compile('^[^' + ''.join(missed) + ']*$')\n",
-      "            self.candidate_words = [w for w in self.candidate_words if exp.match(w)]        "
+      "            empty_target = '_' * len(discovered)\n",
+      "            self.candidate_words = [w for w in self.candidate_words if self.match(empty_target, w, missed)]        "
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 28
+     "prompt_number": 66
     },
     {
      "cell_type": "code",
      "collapsed": false,
      "input": [
-      "class PlayerAdaptivePattern(PlayerAdaptive):\n",
+      "class PlayerAdaptivePatternNoRegex(PlayerAdaptiveNoRegex):\n",
       "    def filter_candidate_words(self, discovered, missed):\n",
       "        attempted_letters = [l for l in discovered if l != '_'] + missed\n",
-      "        if attempted_letters:\n",
-      "            exclusion_pattern = '[^' + ''.join(attempted_letters) + ']'\n",
-      "        else:\n",
-      "            exclusion_pattern = '.'\n",
-      "        exp = re.compile('^' + ''.join(discovered).replace('_', exclusion_pattern) + '$')\n",
-      "        self.candidate_words = [w for w in self.candidate_words if exp.match(w)]"
+      "        self.candidate_words = [w for w in self.candidate_words if self.match(discovered, w, attempted_letters)]"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 29
+     "prompt_number": 67
     },
     {
      "cell_type": "code",
       "\n",
       "wins = 0\n",
       "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveLength(WORDS))\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveLengthNoRegex(WORDS))\n",
       "    g.play_game()\n",
       "    if g.game_won:\n",
       "        wins += 1\n",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "463\n",
+        "471\n",
         "492"
        ]
       },
        "stream": "stdout",
        "text": [
         "\n",
-        "451"
+        "502"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "448"
+        "469"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "1 loops, best of 3: 30.9 s per loop\n"
+        "1 loops, best of 3: 16 s per loop\n"
        ]
       }
      ],
-     "prompt_number": 30
+     "prompt_number": 69
     },
     {
      "cell_type": "code",
       "\n",
       "wins = 0\n",
       "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveIncludedLetters(WORDS))\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveIncludedLettersNoRegex(WORDS))\n",
       "    g.play_game()\n",
       "    if g.game_won:\n",
       "        wins += 1\n",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "985\n",
-        "972"
+        "978\n",
+        "979"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "976"
+        "983"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "986"
+        "979"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "1 loops, best of 3: 1min 8s per loop\n"
+        "1 loops, best of 3: 48 s per loop\n"
        ]
       }
      ],
-     "prompt_number": 31
+     "prompt_number": 70
     },
     {
      "cell_type": "code",
       "\n",
       "wins = 0\n",
       "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveExcludedLetters(WORDS))\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveExcludedLettersNoRegex(WORDS))\n",
       "    g.play_game()\n",
       "    if g.game_won:\n",
       "        wins += 1\n",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "502\n",
-        "491"
+        "582\n",
+        "595"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "537"
+        "587"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "524"
+        "611"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "1 loops, best of 3: 13min 6s per loop\n"
+        "1 loops, best of 3: 4min 59s per loop\n"
        ]
       }
      ],
-     "prompt_number": 32
+     "prompt_number": 71
     },
     {
      "cell_type": "code",
       "\n",
       "wins = 0\n",
       "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAdaptivePattern(WORDS))\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptivePatternNoRegex(WORDS))\n",
       "    g.play_game()\n",
       "    if g.game_won:\n",
       "        wins += 1\n",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "993\n",
-        "991"
+        "991\n",
+        "992"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "994"
+        "993"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "989"
+        "994"
        ]
       },
       {
        "stream": "stdout",
        "text": [
         "\n",
-        "1 loops, best of 3: 56.6 s per loop\n"
-       ]
-      }
-     ],
-     "prompt_number": 33
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "for _ in range(1000):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAdaptivePattern(WORDS))\n",
-      "    g.play_game()\n",
-      "    if not g.game_won:\n",
-      "        print(g.target, g.discovered, g.wrong_letters)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "jutting ['_', 'u', 't', 't', 'i', 'n', 'g'] ['e', 'a', 'o', 'l', 's', 'f', 'p', 'b', 'c', 'r']\n",
-        "faze"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        " ['_', 'a', '_', 'e'] ['r', 'l', 'm', 'p', 's', 'g', 'b', 'd', 'v', 'k']\n",
-        "fate"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        " ['_', 'a', '_', 'e'] ['r', 'l', 'm', 'p', 's', 'g', 'b', 'd', 'v', 'k']\n",
-        "dunk"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        " ['_', 'u', 'n', 'k'] ['e', 's', 'o', 'a', 'i', 'l', 'f', 'r', 'j', 'p']\n",
-        "loons"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        " ['_', 'o', 'o', 'n', 's'] ['e', 't', 'k', 'm', 'p', 'd', 'f', 'c', 'b', 'g']\n",
-        "lab"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        " ['_', 'a', 'b'] ['t', 'p', 'g', 'w', 'm', 'd', 'y', 'r', 'c', 'n']\n",
-        "joked"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        " ['_', 'o', 'k', 'e', 'd'] ['s', 'a', 'w', 'p', 't', 'r', 'h', 'b', 'y', 'c']\n"
-       ]
-      }
-     ],
-     "prompt_number": 34
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "iterations = 10000\n",
-      "wins = 0\n",
-      "for _ in range(iterations):\n",
-      "    g = Game(random.choice(WORDS), player=PlayerAdaptivePattern(WORDS))\n",
-      "    g.play_game()\n",
-      "    if g.game_won:\n",
-      "        wins += 1\n",
-      "print(wins / iterations)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "0.9923\n"
+        "1 loops, best of 3: 37.9 s per loop\n"
        ]
       }
      ],
-     "prompt_number": 35
+     "prompt_number": 72
     },
     {
      "cell_type": "code",
      "input": [],
      "language": "python",
      "metadata": {},
-     "outputs": []
+     "outputs": [],
+     "prompt_number": 72
     }
    ],
    "metadata": {}