Updated notebook versions
[cas-master-teacher-training.git] / hangman / 03-hangman-both.ipynb
index 543d83203262ae3c259f61ec3bc7919182ee7ece..54fbdb4a1c2fdc304e1039b026e9da95d35802ec 100644 (file)
 {
- "metadata": {
-  "name": "",
-  "signature": "sha256:8dfb2bb1cf596c711c837f4c7c2d8706f9dcacc1a0ffc3dc21c1d0fedd3fc6bf"
- },
- "nbformat": 3,
- "nbformat_minor": 0,
- "worksheets": [
+ "cells": [
   {
-   "cells": [
-    {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "#Putting both halves together\n",
-      "We now have systems that can do both halves of the Hangman game. Now it's time to put them together so that we can get a machine to play itself. At the moment, let's concentrate on just the plumbing: let's get the two halves working together. In the next step, we'll think about different strategies and compare their effectiveness.\n",
-      "\n",
-      "The word of this section is \"encapsulation.\" \n",
-      "\n",
-      "First, a change in terminology. We'll call a `player` the thing that makes the guesses (the guesser player from before). We'll call a `game` the thing that handles receiving guesses, updating `discovered` words, updating lives and all of that. That's what was the setting player from before, but without the bit about picking a random word. We'll have the `game` accept the `target` as a parameter, so that we can test everything more easily (if we know what word the `player` is trying to guess, we can predict what it should do).\n",
-      "\n",
-      "Back to encapsulation.\n",
-      "\n",
-      "We want to bundle all the state information for a `game` into one 'thing' so that we don't need to worry about global variables. We will also want to bundle up everything to do with a `player` into one 'thing' so we can tell the game about it. For the `player`, the thing will include both any state it needs and the definition of that `player`'s guessing strategy. We're also going to have a bunch of related players, all doing much the same thing but with different strategies.\n",
-      "\n",
-      "In this case, objects seem like a natural fit.\n",
-      "\n",
-      "(Insert Neil's aikido instructor story here.)"
-     ]
-    },
-    {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "##Object-orientation in Python\n",
-      "There are a few things to remember about object orientation in Python.\n",
-      "\n",
-      "1. It's an ugly kludge that was bolted on long after the language was defined.\n",
-      "\n",
-      "2. `self` needs to be passed in to all object methods as the first parameter, though it doesn't appear when you call the method.\n",
-      "\n",
-      "3. Use `self.whatever` to refer to the instance variable `whatever`\n",
-      "\n",
-      "4. Use `self.however(arg1, arg2)` when one method calls another in the same object.\n",
-      "\n",
-      "Apart from that, it's pretty straightforward."
-     ]
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "import re\n",
-      "import random\n",
-      "import string\n",
-      "import collections"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [],
-     "prompt_number": 43
-    },
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Putting both halves together\n",
+    "We now have systems that can do both halves of the Hangman game. Now it's time to put them together so that we can get a machine to play itself. At the moment, let's concentrate on just the plumbing: let's get the two halves working together. In the next step, we'll think about different strategies and compare their effectiveness.\n",
+    "\n",
+    "The word of this section is \"encapsulation.\" \n",
+    "\n",
+    "First, a change in terminology. We'll call a `player` the thing that makes the guesses (the guesser player from before). We'll call a `game` the thing that handles receiving guesses, updating `discovered` words, updating lives and all of that. That's what was the setting player from before, but without the bit about picking a random word. We'll have the `game` accept the `target` as a parameter, so that we can test everything more easily (if we know what word the `player` is trying to guess, we can predict what it should do).\n",
+    "\n",
+    "Back to encapsulation.\n",
+    "\n",
+    "We want to bundle all the state information for a `game` into one 'thing' so that we don't need to worry about global variables. We will also want to bundle up everything to do with a `player` into one 'thing' so we can tell the game about it. For the `player`, the thing will include both any state it needs and the definition of that `player`'s guessing strategy. We're also going to have a bunch of related players, all doing much the same thing but with different strategies.\n",
+    "\n",
+    "In this case, objects seem like a natural fit.\n",
+    "\n",
+    "(Insert Neil's aikido instructor story here.)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Object-orientation in Python\n",
+    "There are a few things to remember about object orientation in Python.\n",
+    "\n",
+    "1. It's an ugly kludge that was bolted on long after the language was defined.\n",
+    "\n",
+    "2. `self` needs to be passed in to all object methods as the first parameter, though it doesn't appear when you call the method.\n",
+    "\n",
+    "3. Use `self.whatever` to refer to the instance variable `whatever`\n",
+    "\n",
+    "4. Use `self.however(arg1, arg2)` when one method calls another in the same object.\n",
+    "\n",
+    "Apart from that, it's pretty straightforward."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 43,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "import re\n",
+    "import random\n",
+    "import string\n",
+    "import collections"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 44,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "WORDS = [w.strip() for w in open('/usr/share/dict/british-english').readlines() \n",
+    "         if re.match(r'^[a-z]*$', w.strip())]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 45,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "STARTING_LIVES = 10"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "This is pretty much the same code as for the `setter` part, just with plenty of `self` statements scatter around. A few things to note:\n",
+    "\n",
+    "* `initialise()` is now named `__init__()` (note the double underscores fore and aft).\n",
+    "* `player` and `lives` are parameters to the `game`, but with default values.\n",
+    "* I've added two new flags, `game_won` and `game_lost`.\n",
+    "* `do_turn` asks the player for a guess, if there is a player.\n",
+    "* The `guess()` method of the `player` is passed the `lives`, in case the guess should change depending on the progress of the game (the `player` can always ignore it).\n",
+    "* After a guess has been processed, `do_turn` has a few more statements to set the relevant flags to represent the state of the game.\n",
+    "* `report_on_game()` has been added for when the game is being played against a human."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 46,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "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",
+    "        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",
+    "        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"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Let's test it!"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 47,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "g = Game(random.choice(WORDS))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 48,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "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",
+     "data": {
+      "text/plain": [
+       "'distension'"
+      ]
+     },
+     "execution_count": 48,
      "metadata": {},
-     "outputs": [],
-     "prompt_number": 44
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.target"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 49,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "STARTING_LIVES = 10"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "['_', '_', '_', '_', '_', '_', '_', '_', '_', '_']"
+      ]
+     },
+     "execution_count": 49,
      "metadata": {},
-     "outputs": [],
-     "prompt_number": 45
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.discovered"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 50,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "This is pretty much the same code as for the `setter` part, just with plenty of `self` statements scatter around. A few things to note:\n",
-      "\n",
-      "* `initialise()` is now named `__init__()` (note the double underscores fore and aft).\n",
-      "* `player` and `lives` are parameters to the `game`, but with default values.\n",
-      "* I've added two new flags, `game_won` and `game_lost`.\n",
-      "* `do_turn` asks the player for a guess, if there is a player.\n",
-      "* The `guess()` method of the `player` is passed the `lives`, in case the guess should change depending on the progress of the game (the `player` can always ignore it).\n",
-      "* After a guess has been processed, `do_turn` has a few more statements to set the relevant flags to represent the state of the game.\n",
-      "* `report_on_game()` has been added for when the game is being played against a human."
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Word: _ _ _ _ _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n",
+      "Enter letter: e\n"
      ]
-    },
-    {
-     "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",
-      "        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",
-      "        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": 46
-    },
+    }
+   ],
+   "source": [
+    "g.do_turn()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 51,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
+     "data": {
+      "text/plain": [
+       "10"
+      ]
+     },
+     "execution_count": 51,
      "metadata": {},
-     "source": [
-      "Let's test it!"
-     ]
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.lives"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 52,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS))"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "[]"
+      ]
+     },
+     "execution_count": 52,
      "metadata": {},
-     "outputs": [],
-     "prompt_number": 47
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.wrong_letters"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 53,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.target"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 48,
-       "text": [
-        "'distension'"
-       ]
-      }
-     ],
-     "prompt_number": 48
-    },
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Word: _ _ _ _ e _ _ _ _ _  : Lives = 10 , wrong guesses: \n",
+      "Enter letter: q\n"
+     ]
+    }
+   ],
+   "source": [
+    "g.do_turn()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 54,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.discovered"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "9"
+      ]
+     },
+     "execution_count": 54,
      "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 49,
-       "text": [
-        "['_', '_', '_', '_', '_', '_', '_', '_', '_', '_']"
-       ]
-      }
-     ],
-     "prompt_number": 49
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.lives"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 55,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.do_turn()"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "['_', '_', '_', '_', 'e', '_', '_', '_', '_', '_']"
+      ]
+     },
+     "execution_count": 55,
      "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ _ _ _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: e\n"
-       ]
-      }
-     ],
-     "prompt_number": 50
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.discovered"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 56,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.lives"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "['q']"
+      ]
+     },
+     "execution_count": 56,
      "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 51,
-       "text": [
-        "10"
-       ]
-      }
-     ],
-     "prompt_number": 51
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.wrong_letters"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 57,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.wrong_letters"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 52,
-       "text": [
-        "[]"
-       ]
-      }
-     ],
-     "prompt_number": 52
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Word: _ _ _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n",
+      "Enter letter: e\n",
+      "Word: e _ e _ _ _ _ e  : Lives = 10 , wrong guesses: \n",
+      "Enter letter: x\n",
+      "Word: e _ e _ _ _ _ e  : Lives = 9 , wrong guesses: x\n",
+      "Enter letter: s\n",
+      "Word: e _ e _ _ _ _ e  : Lives = 8 , wrong guesses: s x\n",
+      "Enter letter: l\n",
+      "Word: e _ e _ _ _ _ e  : Lives = 7 , wrong guesses: l s x\n",
+      "Enter letter: t\n",
+      "Word: e _ e _ _ _ _ e  : Lives = 6 , wrong guesses: l s t x\n",
+      "Enter letter: m\n",
+      "Word: e _ e _ _ _ _ e  : Lives = 5 , wrong guesses: l m s t x\n",
+      "Enter letter: h\n",
+      "Word: e _ e _ _ _ _ e  : Lives = 4 , wrong guesses: h l m s t x\n",
+      "Enter letter: i\n",
+      "Word: e _ e _ _ _ _ e  : Lives = 3 , wrong guesses: h i l m s t x\n",
+      "Enter letter: a\n",
+      "Word: e _ e _ _ _ _ e  : Lives = 2 , wrong guesses: a h i l m s t x\n",
+      "Enter letter: o\n",
+      "Word: e _ e _ _ o _ e  : Lives = 2 , wrong guesses: a h i l m s t x\n",
+      "Enter letter: n\n",
+      "Word: e _ e _ _ o n e  : Lives = 2 , wrong guesses: a h i l m s t x\n",
+      "Enter letter: r\n",
+      "Word: e _ e r _ o n e  : Lives = 2 , wrong guesses: a h i l m s t x\n",
+      "Enter letter: v\n",
+      "Word: e v e r _ o n e  : Lives = 2 , wrong guesses: a h i l m s t x\n",
+      "Enter letter: y\n",
+      "You won! The word was everyone\n"
+     ]
     },
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.do_turn()"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "True"
+      ]
+     },
+     "execution_count": 57,
      "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ _ _ e _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: q\n"
-       ]
-      }
-     ],
-     "prompt_number": 53
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS))\n",
+    "g.play_game()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 58,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.lives"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 54,
-       "text": [
-        "9"
-       ]
-      }
-     ],
-     "prompt_number": 54
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Word: _ _ _ _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n",
+      "Enter letter: q\n",
+      "Word: _ _ _ _ _ _ _ _ _  : Lives = 9 , wrong guesses: q\n",
+      "Enter letter: z\n",
+      "Word: _ _ _ _ _ _ _ _ _  : Lives = 8 , wrong guesses: q z\n",
+      "Enter letter: x\n",
+      "Word: _ _ _ _ _ _ _ _ _  : Lives = 7 , wrong guesses: q x z\n",
+      "Enter letter: e\n",
+      "Word: _ _ _ _ _ _ _ _ _  : Lives = 6 , wrong guesses: e q x z\n",
+      "Enter letter: a\n",
+      "Word: _ _ a _ _ _ _ _ _  : Lives = 6 , wrong guesses: e q x z\n",
+      "Enter letter: v\n",
+      "Word: _ _ a _ _ _ _ _ _  : Lives = 5 , wrong guesses: e q v x z\n",
+      "Enter letter: b\n",
+      "Word: _ _ a _ _ _ _ _ _  : Lives = 4 , wrong guesses: b e q v x z\n",
+      "Enter letter: k\n",
+      "Word: _ _ a _ k _ k _ _  : Lives = 4 , wrong guesses: b e q v x z\n",
+      "Enter letter: l\n",
+      "Word: _ _ a _ k _ k _ _  : Lives = 3 , wrong guesses: b e l q v x z\n",
+      "Enter letter: j\n",
+      "Word: _ _ a _ k _ k _ _  : Lives = 2 , wrong guesses: b e j l q v x z\n",
+      "Enter letter: p\n",
+      "Word: _ _ a _ k _ k _ _  : Lives = 1 , wrong guesses: b e j l p q v x z\n",
+      "Enter letter: d\n",
+      "You lost. The word was sharkskin\n"
+     ]
     },
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.discovered"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "False"
+      ]
+     },
+     "execution_count": 58,
      "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 55,
-       "text": [
-        "['_', '_', '_', '_', 'e', '_', '_', '_', '_', '_']"
-       ]
-      }
-     ],
-     "prompt_number": 55
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS))\n",
+    "g.play_game()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 59,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.wrong_letters"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "'sharkskin'"
+      ]
+     },
+     "execution_count": 59,
      "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 56,
-       "text": [
-        "['q']"
-       ]
-      }
-     ],
-     "prompt_number": 56
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.target"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Player objects\n",
+    "Let's work ourselves gently into this.\n",
+    "\n",
+    "In the first instance, the only thing a `player` needs is to respond to the `guess` method. Let's make objects with just this method. Let's even make it simpler than the player from the `guesser` stage, and have it just guess letters in alphabetical order."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 60,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "class PlayerAlphabetical:\n",
+    "    def guess(self, discovered, missed, lives):\n",
+    "        return [l for l in string.ascii_lowercase if l not in discovered + missed][0]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "That wasn't too hard, was it?\n",
+    "\n",
+    "Let's use it to play a game."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 69,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "g = Game(random.choice(WORDS), player=PlayerAlphabetical())"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 70,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS))\n",
-      "g.play_game()"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "False"
+      ]
+     },
+     "execution_count": 70,
      "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: e\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ _ _ e  : Lives = 10 , wrong guesses: \n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: x\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ _ _ e  : Lives = 9 , wrong guesses: x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: s\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ _ _ e  : Lives = 8 , wrong guesses: s x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: l\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ _ _ e  : Lives = 7 , wrong guesses: l s x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: t\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ _ _ e  : Lives = 6 , wrong guesses: l s t x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: m\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ _ _ e  : Lives = 5 , wrong guesses: l m s t x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: h\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ _ _ e  : Lives = 4 , wrong guesses: h l m s t x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: i\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ _ _ e  : Lives = 3 , wrong guesses: h i l m s t x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: a\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ _ _ e  : Lives = 2 , wrong guesses: a h i l m s t x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: o\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ o _ e  : Lives = 2 , wrong guesses: a h i l m s t x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: n\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e _ _ o n e  : Lives = 2 , wrong guesses: a h i l m s t x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: r\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e _ e r _ o n e  : Lives = 2 , wrong guesses: a h i l m s t x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: v\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: e v e r _ o n e  : Lives = 2 , wrong guesses: a h i l m s t x\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: y\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "You won! The word was everyone\n"
-       ]
-      },
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 57,
-       "text": [
-        "True"
-       ]
-      }
-     ],
-     "prompt_number": 57
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.play_game()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 71,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS))\n",
-      "g.play_game()"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "['c', 'e', '_', '_', 'i', 'f', 'i', 'c', 'a', '_', 'e', '_']"
+      ]
+     },
+     "execution_count": 71,
      "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ _ _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: q\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ _ _ _ _ _ _ _  : Lives = 9 , wrong guesses: q\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: z\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ _ _ _ _ _ _ _  : Lives = 8 , wrong guesses: q z\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: x\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ _ _ _ _ _ _ _  : Lives = 7 , wrong guesses: q x z\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: e\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ _ _ _ _ _ _ _  : Lives = 6 , wrong guesses: e q x z\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: a\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ a _ _ _ _ _ _  : Lives = 6 , wrong guesses: e q x z\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: v\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ a _ _ _ _ _ _  : Lives = 5 , wrong guesses: e q v x z\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: b\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ a _ _ _ _ _ _  : Lives = 4 , wrong guesses: b e q v x z\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: k\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ a _ k _ k _ _  : Lives = 4 , wrong guesses: b e q v x z\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: l\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ a _ k _ k _ _  : Lives = 3 , wrong guesses: b e l q v x z\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: j\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ a _ k _ k _ _  : Lives = 2 , wrong guesses: b e j l q v x z\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: p\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Word: _ _ a _ k _ k _ _  : Lives = 1 , wrong guesses: b e j l p q v x z\n"
-       ]
-      },
-      {
-       "name": "stdout",
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Enter letter: d\n"
-       ]
-      },
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "You lost. The word was sharkskin\n"
-       ]
-      },
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 58,
-       "text": [
-        "False"
-       ]
-      }
-     ],
-     "prompt_number": 58
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.discovered"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 72,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.target"
-     ],
-     "language": "python",
+     "data": {
+      "text/plain": [
+       "'certificates'"
+      ]
+     },
+     "execution_count": 72,
      "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 59,
-       "text": [
-        "'sharkskin'"
-       ]
-      }
-     ],
-     "prompt_number": 59
-    },
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g.target"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Bored with looking at all the state of the game with different cells. Let's have a `print()` statement that tells us what we need."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 74,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "## Player objects\n",
-      "Let's work ourselves gently into this.\n",
-      "\n",
-      "In the first instance, the only thing a `player` needs is to respond to the `guess` method. Let's make objects with just this method. Let's even make it simpler than the player from the `guesser` stage, and have it just guess letters in alphabetical order."
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Target: turnoffs ; Discovered: ['_', '_', '_', '_', '_', 'f', 'f', '_'] ; Lives remaining: 0\n"
      ]
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "class PlayerAlphabetical:\n",
-      "    def guess(self, discovered, missed, lives):\n",
-      "        return [l for l in string.ascii_lowercase if l not in discovered + missed][0]"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [],
-     "prompt_number": 60
-    },
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS), player=PlayerAlphabetical())\n",
+    "g.play_game()\n",
+    "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Another player\n",
+    "Let's reimplement our player that does letters in frequency order. Again, this is essentially code reused from earlier."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 76,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "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()]\n",
+    "\n",
+    "class PlayerFreqOrdered:\n",
+    "    def guess(self, discovered, missed, lives):\n",
+    "        return [l for l in LETTERS_IN_ORDER if l not in discovered + missed][0]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 77,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "That wasn't too hard, was it?\n",
-      "\n",
-      "Let's use it to play a game."
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Target: cartons ; Discovered: ['c', 'a', 'r', 't', 'o', 'n', 's'] ; Lives remaining: 2\n"
      ]
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS), player=PlayerAlphabetical())"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [],
-     "prompt_number": 69
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.play_game()"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 70,
-       "text": [
-        "False"
-       ]
-      }
-     ],
-     "prompt_number": 70
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.discovered"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 71,
-       "text": [
-        "['c', 'e', '_', '_', 'i', 'f', 'i', 'c', 'a', '_', 'e', '_']"
-       ]
-      }
-     ],
-     "prompt_number": 71
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g.target"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "metadata": {},
-       "output_type": "pyout",
-       "prompt_number": 72,
-       "text": [
-        "'certificates'"
-       ]
-      }
-     ],
-     "prompt_number": 72
-    },
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
+    "g.play_game()\n",
+    "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 78,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "Bored with looking at all the state of the game with different cells. Let's have a `print()` statement that tells us what we need."
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Target: douching ; Discovered: ['d', 'o', 'u', 'c', 'h', 'i', 'n', '_'] ; Lives remaining: 0\n"
      ]
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS), player=PlayerAlphabetical())\n",
-      "g.play_game()\n",
-      "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Target: turnoffs ; Discovered: ['_', '_', '_', '_', '_', 'f', 'f', '_'] ; Lives remaining: 0\n"
-       ]
-      }
-     ],
-     "prompt_number": 74
-    },
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
+    "g.play_game()\n",
+    "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 79,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "### Another player\n",
-      "Let's reimplement our player that does letters in frequency order. Again, this is essentially code reused from earlier."
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Target: minimisation ; Discovered: ['m', 'i', 'n', 'i', 'm', 'i', 's', 'a', 't', 'i', 'o', 'n'] ; Lives remaining: 4\n"
      ]
-    },
-    {
-     "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()]\n",
-      "\n",
-      "class PlayerFreqOrdered:\n",
-      "    def guess(self, discovered, missed, lives):\n",
-      "        return [l for l in LETTERS_IN_ORDER if l not in discovered + missed][0]"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [],
-     "prompt_number": 76
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
-      "g.play_game()\n",
-      "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Target: cartons ; Discovered: ['c', 'a', 'r', 't', 'o', 'n', 's'] ; Lives remaining: 2\n"
-       ]
-      }
-     ],
-     "prompt_number": 77
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
-      "g.play_game()\n",
-      "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Target: douching ; Discovered: ['d', 'o', 'u', 'c', 'h', 'i', 'n', '_'] ; Lives remaining: 0\n"
-       ]
-      }
-     ],
-     "prompt_number": 78
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
-      "g.play_game()\n",
-      "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Target: minimisation ; Discovered: ['m', 'i', 'n', 'i', 'm', 'i', 's', 'a', 't', 'i', 'o', 'n'] ; Lives remaining: 4\n"
-       ]
-      }
-     ],
-     "prompt_number": 79
-    },
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
+    "g.play_game()\n",
+    "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Player subclasses and inheritance\n",
+    "Time to DRY the code a little. Both of these players (`PlayerAlphabetical` and `PlayerFreqOrdered`) are essentially the same. The only difference is the set of letters used in the middle of the `guess` method. \n",
+    "\n",
+    "Let's create a generic \"letters in a fixed order\" class that has that implementation of `guess` and is passed in a set of letters in order. The two players we already have are subclasses of that, where we have pre-packaged letter orders.\n",
+    "\n",
+    "Here's the generic player. Note that the ordered letters are passed in as a parameter to the `__init__()` method, and `guess()` uses that letter sequence."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 85,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "class PlayerFixedOrder:\n",
+    "    def __init__(self, ordered_letters):\n",
+    "        self.ordered_letters = ordered_letters\n",
+    "        \n",
+    "    def guess(self, discovered, missed, lives):\n",
+    "        return [l for l in self.ordered_letters if l not in discovered + missed][0]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can now specify the subclasses, using the fixed order. The initialisation of the subclasses now takes no additional parameter, but we pass the hardcoded order to the superclass's `__init__()`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 86,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "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"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Does it work?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 87,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "## Player subclasses and inheritance\n",
-      "Time to DRY the code a little. Both of these players (`PlayerAlphabetical` and `PlayerFreqOrdered`) are essentially the same. The only difference is the set of letters used in the middle of the `guess` method. \n",
-      "\n",
-      "Let's create a generic \"letters in a fixed order\" class that has that implementation of `guess` and is passed in a set of letters in order. The two players we already have are subclasses of that, where we have pre-packaged letter orders.\n",
-      "\n",
-      "Here's the generic player. Note that the ordered letters are passed in as a parameter to the `__init__()` method, and `guess()` uses that letter sequence."
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Target: yogi ; Discovered: ['_', 'o', '_', 'i'] ; Lives remaining: 0\n"
      ]
-    },
-    {
-     "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",
-      "        return [l for l in self.ordered_letters if l not in discovered + missed][0]"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [],
-     "prompt_number": 85
-    },
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
+    "g.play_game()\n",
+    "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 88,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "We can now specify the subclasses, using the fixed order. The initialisation of the subclasses now takes no additional parameter, but we pass the hardcoded order to the superclass's `__init__()`."
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Target: slimmest ; Discovered: ['s', 'l', 'i', 'm', 'm', 'e', 's', 't'] ; Lives remaining: 3\n"
      ]
-    },
-    {
-     "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": 86
-    },
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
+    "g.play_game()\n",
+    "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 89,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "Does it work?"
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Target: diseases ; Discovered: ['d', 'i', '_', 'e', 'a', '_', 'e', '_'] ; Lives remaining: 0\n"
      ]
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
-      "g.play_game()\n",
-      "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Target: yogi ; Discovered: ['_', 'o', '_', 'i'] ; Lives remaining: 0\n"
-       ]
-      }
-     ],
-     "prompt_number": 87
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
-      "g.play_game()\n",
-      "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Target: slimmest ; Discovered: ['s', 'l', 'i', 'm', 'm', 'e', 's', 't'] ; Lives remaining: 3\n"
-       ]
-      }
-     ],
-     "prompt_number": 88
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS), player=PlayerAlphabetical())\n",
-      "g.play_game()\n",
-      "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Target: diseases ; Discovered: ['d', 'i', '_', 'e', 'a', '_', 'e', '_'] ; Lives remaining: 0\n"
-       ]
-      }
-     ],
-     "prompt_number": 89
-    },
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS), player=PlayerAlphabetical())\n",
+    "g.play_game()\n",
+    "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Can we use the generic version directly?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 90,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "Can we use the generic version directly?"
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Target: pardon ; Discovered: ['p', '_', 'r', '_', 'o', 'n'] ; Lives remaining: 0\n"
      ]
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS), player=PlayerFixedOrder(list(reversed(string.ascii_lowercase))))\n",
-      "g.play_game()\n",
-      "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Target: pardon ; Discovered: ['p', '_', 'r', '_', 'o', 'n'] ; Lives remaining: 0\n"
-       ]
-      }
-     ],
-     "prompt_number": 90
-    },
-    {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [
-      "g = Game(random.choice(WORDS), player=PlayerFixedOrder(list(reversed(string.ascii_lowercase))))\n",
-      "g.play_game()\n",
-      "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
-     ],
-     "language": "python",
-     "metadata": {},
-     "outputs": [
-      {
-       "output_type": "stream",
-       "stream": "stdout",
-       "text": [
-        "Target: grouchier ; Discovered: ['_', 'r', 'o', 'u', '_', '_', '_', '_', 'r'] ; Lives remaining: 0\n"
-       ]
-      }
-     ],
-     "prompt_number": 91
-    },
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS), player=PlayerFixedOrder(list(reversed(string.ascii_lowercase))))\n",
+    "g.play_game()\n",
+    "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 91,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "##Mass gaming\n",
-      "Now we can get machines playing each other, we get get them playing *lots* of games and just tell us the summaries. \n",
-      "\n",
-      "Which of these three methods (alphabetical, frequency ordered, reverse alphabetical) is the best? Let's get each player to play 100 random games and see which gets the most wins."
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Target: grouchier ; Discovered: ['_', 'r', 'o', 'u', '_', '_', '_', '_', 'r'] ; Lives remaining: 0\n"
      ]
-    },
-    {
-     "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": [
-        "49\n"
-       ]
-      }
-     ],
-     "prompt_number": 92
-    },
-    {
-     "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": [
-        "309\n"
-       ]
-      }
-     ],
-     "prompt_number": 96
-    },
+    }
+   ],
+   "source": [
+    "g = Game(random.choice(WORDS), player=PlayerFixedOrder(list(reversed(string.ascii_lowercase))))\n",
+    "g.play_game()\n",
+    "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Mass gaming\n",
+    "Now we can get machines playing each other, we get get them playing *lots* of games and just tell us the summaries. \n",
+    "\n",
+    "Which of these three methods (alphabetical, frequency ordered, reverse alphabetical) is the best? Let's get each player to play 1000 random games and see which gets the most wins."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 92,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "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": [
-        "6\n"
-       ]
-      }
-     ],
-     "prompt_number": 94
-    },
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "49\n"
+     ]
+    }
+   ],
+   "source": [
+    "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)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 96,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "markdown",
-     "metadata": {},
-     "source": [
-      "Frequency ordered is much better than the others, but still only wins about 30% of the time. I'm sure we can do better.\n",
-      "\n",
-      "That's the next part..."
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "309\n"
      ]
-    },
+    }
+   ],
+   "source": [
+    "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)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 94,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [
     {
-     "cell_type": "code",
-     "collapsed": false,
-     "input": [],
-     "language": "python",
-     "metadata": {},
-     "outputs": []
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "6\n"
+     ]
     }
    ],
-   "metadata": {}
+   "source": [
+    "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)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Frequency ordered is much better than the others, but still only wins about 30% of the time. I'm sure we can do better.\n",
+    "\n",
+    "That's the next part..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": []
   }
- ]
-}
\ No newline at end of file
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.5.2+"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}