Done Hangman games
[cas-master-teacher-training.git] / hangman-better.ipynb
diff --git a/hangman-better.ipynb b/hangman-better.ipynb
new file mode 100644 (file)
index 0000000..96c61fe
--- /dev/null
@@ -0,0 +1,1272 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:b93ed7f85302bd806b23e539f31a6d8afd7554cafe052439a06de6aa5a19b08d"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import re\n",
+      "import random\n",
+      "import string\n",
+      "import collections"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 1
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "WORDS = [w.strip() for w in open('/usr/share/dict/british-english').readlines() \n",
+      "         if re.match(r'^[a-z]*$', w.strip())]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 2
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "LETTER_COUNTS = collections.Counter(l.lower() for l in open('sherlock-holmes.txt').read() if l in string.ascii_letters)\n",
+      "LETTERS_IN_ORDER = [p[0] for p in LETTER_COUNTS.most_common()]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 12
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "STARTING_LIVES = 10"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 4
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class Game:\n",
+      "    def __init__(self, target, player=None, lives=STARTING_LIVES):\n",
+      "        self.lives = lives\n",
+      "        self.player = player\n",
+      "        self.target = target\n",
+      "        self.discovered = list('_' * len(target))\n",
+      "        self.wrong_letters = []\n",
+      "        self.game_finished = False\n",
+      "        self.game_won = False\n",
+      "        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",
+      "    \n",
+      "    def update_discovered_word(self, guessed_letter):\n",
+      "        locations = self.find_all(guessed_letter)\n",
+      "        for location in locations:\n",
+      "            self.discovered[location] = guessed_letter\n",
+      "        return self.discovered\n",
+      "    \n",
+      "    def do_turn(self):\n",
+      "        if self.player:\n",
+      "            guess = self.player.guess(self.discovered, self.wrong_letters, self.lives)\n",
+      "        else:\n",
+      "            guess = self.ask_for_guess()\n",
+      "        if guess in self.target:\n",
+      "            self.update_discovered_word(guess)\n",
+      "        else:\n",
+      "            self.lives -= 1\n",
+      "            if guess not in self.wrong_letters:\n",
+      "                self.wrong_letters += [guess]\n",
+      "        if self.lives == 0:\n",
+      "            self.game_finished = True\n",
+      "            self.game_lost = True\n",
+      "        if '_' not in self.discovered:\n",
+      "            self.game_finished = True\n",
+      "            self.game_won = True\n",
+      "            \n",
+      "    def ask_for_guess(self):\n",
+      "        print('Word:', ' '.join(self.discovered), \n",
+      "              ' : Lives =', self.lives, \n",
+      "              ', wrong guesses:', ' '.join(sorted(self.wrong_letters)))\n",
+      "        guess = input('Enter letter: ').strip().lower()[0]\n",
+      "        return guess\n",
+      "    \n",
+      "    def play_game(self):\n",
+      "        self.do_turn()\n",
+      "        while not self.game_finished:\n",
+      "            self.do_turn()\n",
+      "        if not self.player:\n",
+      "            self.report_on_game()\n",
+      "        return self.game_won\n",
+      "    \n",
+      "    def report_on_game(self):\n",
+      "        if self.game_won:\n",
+      "            print('You won! The word was', self.target)\n",
+      "        else:\n",
+      "            print('You lost. The word was', self.target)\n",
+      "        return self.game_won"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 5
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class PlayerFixedOrder:\n",
+      "    def __init__(self, ordered_letters):\n",
+      "        self.ordered_letters = ordered_letters\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",
+      "\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:]"
+     ],
+     "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": [
+      "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": [
+        "55\n"
+       ]
+      }
+     ],
+     "prompt_number": 8
+    },
+    {
+     "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)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "336\n"
+       ]
+      }
+     ],
+     "prompt_number": 9
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "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": [
+        "4\n"
+       ]
+      }
+     ],
+     "prompt_number": 10
+    },
+    {
+     "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()]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 17
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "DICT_COUNTS"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 18,
+       "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})"
+       ]
+      }
+     ],
+     "prompt_number": 18
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print(DICT_LETTERS_IN_ORDER)\n",
+      "print(LETTERS_IN_ORDER)"
+     ],
+     "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": 19
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "wins = 0\n",
+      "for _ in range(1000):\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",
+      "print(wins)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "451\n"
+       ]
+      }
+     ],
+     "prompt_number": 20
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "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",
+      "        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",
+      "    \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": 33
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "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": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "485\n"
+       ]
+      }
+     ],
+     "prompt_number": 34
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "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",
+      "        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",
+      "    \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": [],
+     "prompt_number": 51
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "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": [
+        "985\n"
+       ]
+      }
+     ],
+     "prompt_number": 52
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "re.match('^[^xaz]*$', 'happy')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 50
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class PlayerAdaptiveExcludedLetters:\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.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",
+      "    \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": 62
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "wins = 0\n",
+      "for _ in range(1000):\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptiveExcludedLetters(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": [
+        "491\n"
+       ]
+      }
+     ],
+     "prompt_number": 63
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "g.player.candidate_words"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 59,
+       "text": [
+        "['a']"
+       ]
+      }
+     ],
+     "prompt_number": 59
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "g.wrong_letters"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 61,
+       "text": [
+        "['a']"
+       ]
+      }
+     ],
+     "prompt_number": 61
+    },
+    {
+     "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",
+      "\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",
+      "        if missed:\n",
+      "            exclusion_pattern = '(?!.*[' + ''.join(missed) + '])'\n",
+      "        else:\n",
+      "            exclusion_pattern = ''\n",
+      "        exp = re.compile('^' + exclusion_pattern + ''.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": [],
+     "prompt_number": 109
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def fcw(words, discovered, missed):\n",
+      "    if missed:\n",
+      "        exclusion_pattern = '(?!.*[' + ''.join(missed) + '])'\n",
+      "    else:\n",
+      "        exclusion_pattern = ''\n",
+      "    exp = re.compile('^' + exclusion_pattern + ''.join(discovered).replace('_', '.') + '$')\n",
+      "    return [w for w in words if exp.match(w)]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 97
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "def fcwp(discovered, missed):\n",
+      "    if missed:\n",
+      "        exclusion_pattern = '(?!.*[' + ''.join(missed) + '])'\n",
+      "    else:\n",
+      "        exclusion_pattern = ''\n",
+      "    return '^' + exclusion_pattern + ''.join(discovered).replace('_', '.') + '$'"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 102
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "fcwp(['h', '_', 'p', '_'], ['x', 'w'])"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 103,
+       "text": [
+        "'^(?!.*[xw])h.p.$'"
+       ]
+      }
+     ],
+     "prompt_number": 103
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "re.match('^(?!.*[xw])h.p.$', 'hwpe')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 101
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "re.match('^(?!.*[xw])h.p.$', 'hape')"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 104,
+       "text": [
+        "<_sre.SRE_Match object; span=(0, 4), match='hape'>"
+       ]
+      }
+     ],
+     "prompt_number": 104
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "fcw(WORDS, ['h', '_', 'p', '_'], ['x', 'w', 's'])"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 108,
+       "text": [
+        "['hope', 'hype', 'hypo']"
+       ]
+      }
+     ],
+     "prompt_number": 108
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "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": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "992\n"
+       ]
+      }
+     ],
+     "prompt_number": 110
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%%timeit\n",
+      "\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": [
+        "984\n",
+        "979"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "982"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "979"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 52.9 s per loop\n"
+       ]
+      }
+     ],
+     "prompt_number": 111
+    },
+    {
+     "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.play_game()\n",
+      "    if g.game_won:\n",
+      "        wins += 1\n",
+      "print(wins)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "986\n",
+        "991"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "989"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "989"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 44.7 s per loop\n"
+       ]
+      }
+     ],
+     "prompt_number": 112
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "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(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",
+      "    \n",
+      "    def filter_candidate_words(self, discovered, missed):\n",
+      "        pass\n",
+      "        \n",
+      "    def set_ordered_letters(self):\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": 174
+    },
+    {
+     "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": 175
+    },
+    {
+     "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": 176
+    },
+    {
+     "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": 177
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "class PlayerAdaptivePatternNegLookahead(PlayerAdaptive):\n",
+      "    def filter_candidate_words(self, discovered, missed):\n",
+      "        if missed:\n",
+      "            exclusion_pattern = '(?!.*[' + ''.join(missed) + '])'\n",
+      "        else:\n",
+      "            exclusion_pattern = ''\n",
+      "        exp = re.compile('^' + exclusion_pattern + ''.join(discovered).replace('_', '.') + '$')\n",
+      "        self.candidate_words = [w for w in self.candidate_words if exp.match(w)]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 195
+    },
+    {
+     "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": 196
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%%timeit\n",
+      "\n",
+      "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": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "453\n",
+        "494"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "505"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "477"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 24.3 s per loop\n"
+       ]
+      }
+     ],
+     "prompt_number": 179
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%%timeit\n",
+      "\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": [
+        "984\n",
+        "983"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "985"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "982"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 52.9 s per loop\n"
+       ]
+      }
+     ],
+     "prompt_number": 180
+    },
+    {
+     "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.play_game()\n",
+      "    if g.game_won:\n",
+      "        wins += 1\n",
+      "print(wins)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "535\n",
+        "509"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "519"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "507"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 11min 14s per loop\n"
+       ]
+      }
+     ],
+     "prompt_number": 181
+    },
+    {
+     "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.play_game()\n",
+      "    if g.game_won:\n",
+      "        wins += 1\n",
+      "print(wins)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "993\n",
+        "990"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "992"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "994"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 44.1 s per loop\n"
+       ]
+      }
+     ],
+     "prompt_number": 197
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%%timeit\n",
+      "\n",
+      "wins = 0\n",
+      "for _ in range(1000):\n",
+      "    g = Game(random.choice(WORDS), player=PlayerAdaptivePatternNegLookahead(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": [
+        "989\n",
+        "993"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "994"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "993"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "1 loops, best of 3: 46 s per loop\n"
+       ]
+      }
+     ],
+     "prompt_number": 198
+    },
+    {
+     "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": [
+        "rutted ['_', 'u', 't', 't', 'e', 'd'] ['a', 'o', 'i', 'l', 's', 'g', 'b', 'j', 'n', 'p']\n",
+        "cur"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'u', '_'] ['a', 'o', 'e', 'i', 'b', 'g', 'n', 'm', 'p', 't']\n",
+        "wiles"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'i', '_', 'e', 's'] ['a', 'm', 'n', 'v', 't', 'r', 'k', 'f', 'p', 'd']\n",
+        "oak"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        " ['_', 'a', '_'] ['t', 'p', 'g', 'w', 'd', 'y', 'r', 'm', 'b', 's']\n"
+       ]
+      }
+     ],
+     "prompt_number": 217
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [],
+     "language": "python",
+     "metadata": {},
+     "outputs": []
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file