- "cells": [
- {
- "cell_type": "code",
- "collapsed": false,
- "input": [
- "import re\n",
- "import random\n",
- "import string\n",
- "import collections"
- ],
- "language": "python",
- "metadata": {},
- "outputs": [],
- "prompt_number": 71
- },
- {
- "cell_type": "code",
- "collapsed": false,
- "input": [
- "WORDS = [w.strip() for w in open('/usr/share/dict/british-english').readlines() \n",
- " if re.match(r'^[a-z]*$', w.strip())]"
- ],
- "language": "python",
- "metadata": {},
- "outputs": [],
- "prompt_number": 72
- },
- {
- "cell_type": "code",
- "collapsed": false,
- "input": [
- "STARTING_LIVES = 10"
- ],
- "language": "python",
- "metadata": {},
- "outputs": [],
- "prompt_number": 73
- },
- {
- "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": 74
- },
- {
- "cell_type": "code",
- "collapsed": false,
- "input": [
- "g = Game(random.choice(WORDS))"
- ],
- "language": "python",
- "metadata": {},
- "outputs": [],
- "prompt_number": 75
- },
- {
- "cell_type": "code",
- "collapsed": false,
- "input": [
- "g.target"
- ],
- "language": "python",
- "metadata": {},
- "outputs": [
- {
- "metadata": {},
- "output_type": "pyout",
- "prompt_number": 76,
- "text": [
- "'strife'"
- ]
- }
- ],
- "prompt_number": 76
- },
- {
- "cell_type": "code",
- "collapsed": false,
- "input": [
- "g.discovered"
- ],
- "language": "python",
- "metadata": {},
- "outputs": [
- {
- "metadata": {},
- "output_type": "pyout",
- "prompt_number": 77,
- "text": [
- "['_', '_', '_', '_', '_', '_']"
- ]
- }
- ],
- "prompt_number": 77
- },
- {
- "cell_type": "code",
- "collapsed": false,
- "input": [
- "g.do_turn()"
- ],
- "language": "python",
- "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": 78
- },
- {
- "cell_type": "code",
- "collapsed": false,
- "input": [
- "g.lives"
- ],
- "language": "python",
- "metadata": {},
- "outputs": [
- {
- "metadata": {},
- "output_type": "pyout",
- "prompt_number": 79,
- "text": [
- "10"
- ]
- }
- ],
- "prompt_number": 79
- },
+ "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": [