Finished the text
[cas-master-teacher-training.git] / hangman / 03-hangman-both.ipynb
index d9e565b688e142d8c9b8541c8603a873c5ac5429..543d83203262ae3c259f61ec3bc7919182ee7ece 100644 (file)
@@ -1,13 +1,51 @@
 {
  "metadata": {
   "name": "",
-  "signature": "sha256:5e9f030b42097ebbcce2654cdb1d9370fde38b61bdbfa85a0cc700e76d5a8a71"
+  "signature": "sha256:8dfb2bb1cf596c711c837f4c7c2d8706f9dcacc1a0ffc3dc21c1d0fedd3fc6bf"
  },
  "nbformat": 3,
  "nbformat_minor": 0,
  "worksheets": [
   {
    "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,
@@ -20,7 +58,7 @@
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 71
+     "prompt_number": 43
     },
     {
      "cell_type": "code",
@@ -32,7 +70,7 @@
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 72
+     "prompt_number": 44
     },
     {
      "cell_type": "code",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 73
+     "prompt_number": 45
+    },
+    {
+     "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",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 74
+     "prompt_number": 46
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Let's test it!"
+     ]
     },
     {
      "cell_type": "code",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 75
+     "prompt_number": 47
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 76,
+       "prompt_number": 48,
        "text": [
-        "'strife'"
+        "'distension'"
        ]
       }
      ],
-     "prompt_number": 76
+     "prompt_number": 48
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 77,
+       "prompt_number": 49,
        "text": [
-        "['_', '_', '_', '_', '_', '_']"
+        "['_', '_', '_', '_', '_', '_', '_', '_', '_', '_']"
        ]
       }
      ],
-     "prompt_number": 77
+     "prompt_number": 49
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
+        "Word: _ _ _ _ _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
        ]
       },
       {
        ]
       }
      ],
-     "prompt_number": 78
+     "prompt_number": 50
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 79,
+       "prompt_number": 51,
        "text": [
         "10"
        ]
       }
      ],
-     "prompt_number": 79
+     "prompt_number": 51
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 80,
+       "prompt_number": 52,
        "text": [
         "[]"
        ]
       }
      ],
-     "prompt_number": 80
+     "prompt_number": 52
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ _ e  : Lives = 10 , wrong guesses: \n"
+        "Word: _ _ _ _ e _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: d\n"
+        "Enter letter: q\n"
        ]
       }
      ],
-     "prompt_number": 81
+     "prompt_number": 53
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 82,
+       "prompt_number": 54,
        "text": [
         "9"
        ]
       }
      ],
-     "prompt_number": 82
+     "prompt_number": 54
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 83,
+       "prompt_number": 55,
        "text": [
-        "['_', '_', '_', '_', '_', 'e']"
+        "['_', '_', '_', '_', 'e', '_', '_', '_', '_', '_']"
        ]
       }
      ],
-     "prompt_number": 83
+     "prompt_number": 55
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 84,
+       "prompt_number": 56,
        "text": [
-        "['d']"
+        "['q']"
        ]
       }
      ],
-     "prompt_number": 84
+     "prompt_number": 56
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
+        "Word: _ _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: d\n"
+        "Enter letter: e\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ _ _ d  : Lives = 10 , wrong guesses: \n"
+        "Word: e _ e _ _ _ _ e  : Lives = 10 , wrong guesses: \n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: e\n"
+        "Enter letter: x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ e _ _ _ e d  : Lives = 10 , wrong guesses: \n"
+        "Word: e _ e _ _ _ _ e  : Lives = 9 , wrong guesses: x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: f\n"
+        "Enter letter: s\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ e _ _ _ e d  : Lives = 9 , wrong guesses: f\n"
+        "Word: e _ e _ _ _ _ e  : Lives = 8 , wrong guesses: s x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: e\n"
+        "Enter letter: l\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ e _ _ _ e d  : Lives = 9 , wrong guesses: f\n"
+        "Word: e _ e _ _ _ _ e  : Lives = 7 , wrong guesses: l s x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: s\n"
+        "Enter letter: t\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ e _ _ _ e d  : Lives = 8 , wrong guesses: f s\n"
+        "Word: e _ e _ _ _ _ e  : Lives = 6 , wrong guesses: l s t x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: t\n"
+        "Enter letter: m\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ e _ _ _ e d  : Lives = 7 , wrong guesses: f s t\n"
+        "Word: e _ e _ _ _ _ e  : Lives = 5 , wrong guesses: l m s t x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: n\n"
+        "Enter letter: h\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ e _ _ _ e d  : Lives = 6 , wrong guesses: f n s t\n"
+        "Word: e _ e _ _ _ _ e  : Lives = 4 , wrong guesses: h l m s t x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: m\n"
+        "Enter letter: i\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ e _ _ _ e d  : Lives = 5 , wrong guesses: f m n s t\n"
+        "Word: e _ e _ _ _ _ e  : Lives = 3 , wrong guesses: h i l m s t x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: h\n"
+        "Enter letter: a\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ e _ _ _ e d  : Lives = 4 , wrong guesses: f h m n s t\n"
+        "Word: e _ e _ _ _ _ e  : Lives = 2 , wrong guesses: a h i l m s t x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ e _ _ _ e d  : Lives = 3 , wrong guesses: f h m n o s t\n"
+        "Word: e _ e _ _ o _ e  : Lives = 2 , wrong guesses: a h i l m s t x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: i\n"
+        "Enter letter: n\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ e _ i _ e d  : Lives = 3 , wrong guesses: f h m n o s t\n"
+        "Word: e _ e _ _ o n e  : Lives = 2 , wrong guesses: a h i l m s t x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: r e _ i r e d  : Lives = 3 , wrong guesses: f h m n o s t\n"
+        "Word: e _ e r _ o n e  : Lives = 2 , wrong guesses: a h i l m s t x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: w\n"
+        "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 rewired\n"
+        "You won! The word was everyone\n"
        ]
       },
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 85,
+       "prompt_number": 57,
        "text": [
         "True"
        ]
       }
      ],
-     "prompt_number": 85
+     "prompt_number": 57
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
+        "Word: _ _ _ _ _ _ _ _ _  : Lives = 10 , wrong guesses: \n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: e\n"
+        "Enter letter: q\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ _  : Lives = 9 , wrong guesses: e\n"
+        "Word: _ _ _ _ _ _ _ _ _  : Lives = 9 , wrong guesses: q\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: s\n"
+        "Enter letter: z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ s  : Lives = 9 , wrong guesses: e\n"
+        "Word: _ _ _ _ _ _ _ _ _  : Lives = 8 , wrong guesses: q z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: t\n"
+        "Enter letter: x\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ s  : Lives = 8 , wrong guesses: e t\n"
+        "Word: _ _ _ _ _ _ _ _ _  : Lives = 7 , wrong guesses: q x z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: o\n"
+        "Enter letter: e\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ s  : Lives = 7 , wrong guesses: e o t\n"
+        "Word: _ _ _ _ _ _ _ _ _  : Lives = 6 , wrong guesses: e q x z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: i\n"
+        "Enter letter: a\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ s  : Lives = 6 , wrong guesses: e i o t\n"
+        "Word: _ _ a _ _ _ _ _ _  : Lives = 6 , wrong guesses: e q x z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: u\n"
+        "Enter letter: v\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ s  : Lives = 5 , wrong guesses: e i o t u\n"
+        "Word: _ _ a _ _ _ _ _ _  : Lives = 5 , wrong guesses: e q v x z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: a\n"
+        "Enter letter: b\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ _ _ _ s  : Lives = 4 , wrong guesses: a e i o t u\n"
+        "Word: _ _ a _ _ _ _ _ _  : Lives = 4 , wrong guesses: b e q v x z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: y\n"
+        "Enter letter: k\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ y _ _ s  : Lives = 4 , wrong guesses: a e i o t u\n"
+        "Word: _ _ a _ k _ k _ _  : Lives = 4 , wrong guesses: b e q v x z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: w\n"
+        "Enter letter: l\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: _ y _ _ s  : Lives = 3 , wrong guesses: a e i o t u w\n"
+        "Word: _ _ a _ k _ k _ _  : Lives = 3 , wrong guesses: b e l q v x z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: h\n"
+        "Enter letter: j\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: h y _ _ s  : Lives = 3 , wrong guesses: a e i o t u w\n"
+        "Word: _ _ a _ k _ k _ _  : Lives = 2 , wrong guesses: b e j l q v x z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: m\n"
+        "Enter letter: p\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Word: h y m _ s  : Lives = 3 , wrong guesses: a e i o t u w\n"
+        "Word: _ _ a _ k _ k _ _  : Lives = 1 , wrong guesses: b e j l p q v x z\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Enter letter: n\n"
+        "Enter letter: d\n"
        ]
       },
       {
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "You won! The word was hymns\n"
+        "You lost. The word was sharkskin\n"
        ]
       },
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 86,
+       "prompt_number": 58,
        "text": [
-        "True"
+        "False"
        ]
       }
      ],
-     "prompt_number": 86
+     "prompt_number": 58
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 108,
+       "prompt_number": 59,
        "text": [
-        "'six'"
+        "'sharkskin'"
        ]
       }
      ],
-     "prompt_number": 108
+     "prompt_number": 59
+    },
+    {
+     "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",
      "input": [
       "class PlayerAlphabetical:\n",
       "    def guess(self, discovered, missed, lives):\n",
-      "        guessed_letters = [l.lower() for l in discovered + missed if l in string.ascii_letters]\n",
-      "        return [l for l in string.ascii_lowercase if l not in guessed_letters][0]"
+      "        return [l for l in string.ascii_lowercase if l not in discovered + missed][0]"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 109
+     "prompt_number": 60
+    },
+    {
+     "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",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 110
+     "prompt_number": 69
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 111,
+       "prompt_number": 70,
        "text": [
         "False"
        ]
       }
      ],
-     "prompt_number": 111
+     "prompt_number": 70
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 112,
+       "prompt_number": 71,
        "text": [
-        "['l', 'e', 'g', 'a', 'l', 'l', '_']"
+        "['c', 'e', '_', '_', 'i', 'f', 'i', 'c', 'a', '_', 'e', '_']"
        ]
       }
      ],
-     "prompt_number": 112
+     "prompt_number": 71
     },
     {
      "cell_type": "code",
       {
        "metadata": {},
        "output_type": "pyout",
-       "prompt_number": 113,
+       "prompt_number": 72,
        "text": [
-        "'legally'"
+        "'certificates'"
        ]
       }
      ],
-     "prompt_number": 113
+     "prompt_number": 72
+    },
+    {
+     "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",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Target: cavalry ; Discovered: ['c', 'a', '_', 'a', 'l', '_', '_'] ; Lives remaining: 0\n"
+        "Target: turnoffs ; Discovered: ['_', '_', '_', '_', '_', 'f', 'f', '_'] ; Lives remaining: 0\n"
        ]
       }
      ],
-     "prompt_number": 114
+     "prompt_number": 74
+    },
+    {
+     "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",
      "collapsed": false,
      "input": [
-      "LETTER_COUNTS = collections.Counter(l.lower() for l in open('sherlock-holmes.txt').read() if l in string.ascii_letters)\n",
+      "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",
-      "        guessed_letters = [l.lower() for l in discovered + missed if l in string.ascii_letters]\n",
-      "        return [l for l in LETTERS_IN_ORDER if l not in guessed_letters][0]"
+      "        return [l for l in LETTERS_IN_ORDER if l not in discovered + missed][0]"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 115
+     "prompt_number": 76
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Target: deathblow ; Discovered: ['d', 'e', 'a', 't', 'h', '_', 'l', 'o', 'w'] ; Lives remaining: 0\n"
+        "Target: cartons ; Discovered: ['c', 'a', 'r', 't', 'o', 'n', 's'] ; Lives remaining: 2\n"
        ]
       }
      ],
-     "prompt_number": 116
+     "prompt_number": 77
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Target: littered ; Discovered: ['l', 'i', 't', 't', 'e', 'r', 'e', 'd'] ; Lives remaining: 5\n"
+        "Target: douching ; Discovered: ['d', 'o', 'u', 'c', 'h', 'i', 'n', '_'] ; Lives remaining: 0\n"
        ]
       }
      ],
-     "prompt_number": 117
+     "prompt_number": 78
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Target: licenced ; Discovered: ['l', 'i', 'c', 'e', 'n', 'c', 'e', 'd'] ; Lives remaining: 1\n"
+        "Target: minimisation ; Discovered: ['m', 'i', 'n', 'i', 'm', 'i', 's', 'a', 't', 'i', 'o', 'n'] ; Lives remaining: 4\n"
        ]
       }
      ],
-     "prompt_number": 118
+     "prompt_number": 79
+    },
+    {
+     "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",
       "        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",
-      "        return [l for l in self.ordered_letters if l not in guessed_letters][0]"
+      "        return [l for l in self.ordered_letters if l not in discovered + missed][0]"
      ],
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 119
+     "prompt_number": 85
+    },
+    {
+     "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",
      "language": "python",
      "metadata": {},
      "outputs": [],
-     "prompt_number": 120
+     "prompt_number": 86
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Does it work?"
+     ]
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Target: unworthier ; Discovered: ['u', 'n', 'w', 'o', 'r', 't', 'h', 'i', 'e', 'r'] ; Lives remaining: 5\n"
+        "Target: yogi ; Discovered: ['_', 'o', '_', 'i'] ; Lives remaining: 0\n"
        ]
       }
      ],
-     "prompt_number": 121
+     "prompt_number": 87
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Target: kneel ; Discovered: ['_', 'n', 'e', 'e', 'l'] ; Lives remaining: 0\n"
+        "Target: slimmest ; Discovered: ['s', 'l', 'i', 'm', 'm', 'e', 's', 't'] ; Lives remaining: 3\n"
        ]
       }
      ],
-     "prompt_number": 122
+     "prompt_number": 88
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Target: provisoes ; Discovered: ['_', '_', '_', '_', 'i', '_', '_', 'e', '_'] ; Lives remaining: 0\n"
+        "Target: diseases ; Discovered: ['d', 'i', '_', 'e', 'a', '_', 'e', '_'] ; Lives remaining: 0\n"
        ]
       }
      ],
-     "prompt_number": 123
+     "prompt_number": 89
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Can we use the generic version directly?"
+     ]
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Target: invincible ; Discovered: ['_', '_', 'v', '_', '_', '_', '_', '_', '_', '_'] ; Lives remaining: 0\n"
+        "Target: pardon ; Discovered: ['p', '_', 'r', '_', 'o', 'n'] ; Lives remaining: 0\n"
        ]
       }
      ],
-     "prompt_number": 124
+     "prompt_number": 90
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "Target: clogging ; Discovered: ['_', '_', '_', '_', '_', '_', '_', '_'] ; Lives remaining: 0\n"
+        "Target: grouchier ; Discovered: ['_', 'r', 'o', 'u', '_', '_', '_', '_', 'r'] ; Lives remaining: 0\n"
        ]
       }
      ],
-     "prompt_number": 125
+     "prompt_number": 91
+    },
+    {
+     "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."
+     ]
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "44\n"
+        "49\n"
        ]
       }
      ],
-     "prompt_number": 126
+     "prompt_number": 92
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "320\n"
+        "309\n"
        ]
       }
      ],
-     "prompt_number": 127
+     "prompt_number": 96
     },
     {
      "cell_type": "code",
        "output_type": "stream",
        "stream": "stdout",
        "text": [
-        "7\n"
+        "6\n"
        ]
       }
      ],
-     "prompt_number": 128
+     "prompt_number": 94
+    },
+    {
+     "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",
      "input": [],
      "language": "python",
      "metadata": {},
-     "outputs": [],
-     "prompt_number": 128
+     "outputs": []
     }
    ],
    "metadata": {}