X-Git-Url: https://git.njae.me.uk/?a=blobdiff_plain;f=hangman%2F03-hangman-both.ipynb;fp=hangman%2F03-hangman-both.ipynb;h=543d83203262ae3c259f61ec3bc7919182ee7ece;hb=ff6fbe5359c326d085cdd0f7e6e7f359c0d99064;hp=d9e565b688e142d8c9b8541c8603a873c5ac5429;hpb=b46905269db141a9aa4b4b8d3dee6a630925d6b3;p=cas-master-teacher-training.git diff --git a/hangman/03-hangman-both.ipynb b/hangman/03-hangman-both.ipynb index d9e565b..543d832 100644 --- a/hangman/03-hangman-both.ipynb +++ b/hangman/03-hangman-both.ipynb @@ -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", @@ -43,7 +81,22 @@ "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", @@ -112,7 +165,14 @@ "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 74 + "prompt_number": 46 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's test it!" + ] }, { "cell_type": "code", @@ -123,7 +183,7 @@ "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 75 + "prompt_number": 47 }, { "cell_type": "code", @@ -137,13 +197,13 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 76, + "prompt_number": 48, "text": [ - "'strife'" + "'distension'" ] } ], - "prompt_number": 76 + "prompt_number": 48 }, { "cell_type": "code", @@ -157,13 +217,13 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 77, + "prompt_number": 49, "text": [ - "['_', '_', '_', '_', '_', '_']" + "['_', '_', '_', '_', '_', '_', '_', '_', '_', '_']" ] } ], - "prompt_number": 77 + "prompt_number": 49 }, { "cell_type": "code", @@ -178,7 +238,7 @@ "output_type": "stream", "stream": "stdout", "text": [ - "Word: _ _ _ _ _ _ : Lives = 10 , wrong guesses: \n" + "Word: _ _ _ _ _ _ _ _ _ _ : Lives = 10 , wrong guesses: \n" ] }, { @@ -190,7 +250,7 @@ ] } ], - "prompt_number": 78 + "prompt_number": 50 }, { "cell_type": "code", @@ -204,13 +264,13 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 79, + "prompt_number": 51, "text": [ "10" ] } ], - "prompt_number": 79 + "prompt_number": 51 }, { "cell_type": "code", @@ -224,13 +284,13 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 80, + "prompt_number": 52, "text": [ "[]" ] } ], - "prompt_number": 80 + "prompt_number": 52 }, { "cell_type": "code", @@ -245,7 +305,7 @@ "output_type": "stream", "stream": "stdout", "text": [ - "Word: _ _ _ _ _ e : Lives = 10 , wrong guesses: \n" + "Word: _ _ _ _ e _ _ _ _ _ : Lives = 10 , wrong guesses: \n" ] }, { @@ -253,11 +313,11 @@ "output_type": "stream", "stream": "stdout", "text": [ - "Enter letter: d\n" + "Enter letter: q\n" ] } ], - "prompt_number": 81 + "prompt_number": 53 }, { "cell_type": "code", @@ -271,13 +331,13 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 82, + "prompt_number": 54, "text": [ "9" ] } ], - "prompt_number": 82 + "prompt_number": 54 }, { "cell_type": "code", @@ -291,13 +351,13 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 83, + "prompt_number": 55, "text": [ - "['_', '_', '_', '_', '_', 'e']" + "['_', '_', '_', '_', 'e', '_', '_', '_', '_', '_']" ] } ], - "prompt_number": 83 + "prompt_number": 55 }, { "cell_type": "code", @@ -311,13 +371,13 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 84, + "prompt_number": 56, "text": [ - "['d']" + "['q']" ] } ], - "prompt_number": 84 + "prompt_number": 56 }, { "cell_type": "code", @@ -333,7 +393,7 @@ "output_type": "stream", "stream": "stdout", "text": [ - "Word: _ _ _ _ _ _ _ : Lives = 10 , wrong guesses: \n" + "Word: _ _ _ _ _ _ _ _ : Lives = 10 , wrong guesses: \n" ] }, { @@ -341,14 +401,14 @@ "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" ] }, { @@ -356,14 +416,14 @@ "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" ] }, { @@ -371,14 +431,14 @@ "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" ] }, { @@ -386,14 +446,14 @@ "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" ] }, { @@ -401,14 +461,14 @@ "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" ] }, { @@ -416,14 +476,14 @@ "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" ] }, { @@ -431,14 +491,14 @@ "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" ] }, { @@ -446,14 +506,14 @@ "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" ] }, { @@ -461,14 +521,14 @@ "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" ] }, { @@ -483,7 +543,7 @@ "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" ] }, { @@ -491,14 +551,14 @@ "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" ] }, { @@ -513,7 +573,7 @@ "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" ] }, { @@ -521,26 +581,41 @@ "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", @@ -556,7 +631,7 @@ "output_type": "stream", "stream": "stdout", "text": [ - "Word: _ _ _ _ _ : Lives = 10 , wrong guesses: \n" + "Word: _ _ _ _ _ _ _ _ _ : Lives = 10 , wrong guesses: \n" ] }, { @@ -564,14 +639,14 @@ "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" ] }, { @@ -579,14 +654,14 @@ "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" ] }, { @@ -594,14 +669,14 @@ "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" ] }, { @@ -609,14 +684,14 @@ "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" ] }, { @@ -624,14 +699,14 @@ "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" ] }, { @@ -639,14 +714,14 @@ "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" ] }, { @@ -654,14 +729,14 @@ "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" ] }, { @@ -669,14 +744,14 @@ "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" ] }, { @@ -684,14 +759,14 @@ "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" ] }, { @@ -699,14 +774,14 @@ "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" ] }, { @@ -714,14 +789,14 @@ "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" ] }, { @@ -729,26 +804,26 @@ "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", @@ -762,13 +837,23 @@ { "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", @@ -776,13 +861,21 @@ "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", @@ -793,7 +886,7 @@ "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 110 + "prompt_number": 69 }, { "cell_type": "code", @@ -807,13 +900,13 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 111, + "prompt_number": 70, "text": [ "False" ] } ], - "prompt_number": 111 + "prompt_number": 70 }, { "cell_type": "code", @@ -827,13 +920,13 @@ { "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", @@ -847,13 +940,20 @@ { "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", @@ -870,28 +970,35 @@ "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", @@ -908,11 +1015,11 @@ "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", @@ -929,11 +1036,11 @@ "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", @@ -950,11 +1057,23 @@ "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", @@ -965,13 +1084,19 @@ " 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", @@ -988,7 +1113,14 @@ "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 120 + "prompt_number": 86 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Does it work?" + ] }, { "cell_type": "code", @@ -1005,11 +1137,11 @@ "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", @@ -1026,11 +1158,11 @@ "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", @@ -1047,11 +1179,18 @@ "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", @@ -1068,11 +1207,11 @@ "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", @@ -1089,11 +1228,21 @@ "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", @@ -1114,11 +1263,11 @@ "output_type": "stream", "stream": "stdout", "text": [ - "44\n" + "49\n" ] } ], - "prompt_number": 126 + "prompt_number": 92 }, { "cell_type": "code", @@ -1139,11 +1288,11 @@ "output_type": "stream", "stream": "stdout", "text": [ - "320\n" + "309\n" ] } ], - "prompt_number": 127 + "prompt_number": 96 }, { "cell_type": "code", @@ -1164,11 +1313,20 @@ "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", @@ -1176,8 +1334,7 @@ "input": [], "language": "python", "metadata": {}, - "outputs": [], - "prompt_number": 128 + "outputs": [] } ], "metadata": {}