First bit of the A-level miscellany
[cas-master-teacher-training.git] / hangman / 02-hangman-guesser.ipynb
1 {
2 "metadata": {
3 "name": "",
4 "signature": "sha256:eb35fa497310659186b8d082192529c5f3ce0083adaa1704282cce83da804db6"
5 },
6 "nbformat": 3,
7 "nbformat_minor": 0,
8 "worksheets": [
9 {
10 "cells": [
11 {
12 "cell_type": "markdown",
13 "metadata": {},
14 "source": [
15 "# Hangman 2: the guesser\n",
16 "Note that we're not trying to do anything clever here. We're just trying to make something that makes a series of legal moves in the game and that sometimes might win. We want to get something that fits the player-sized hole when we put the setter and guesser together. The clever players come later. \n",
17 "\n",
18 "We also need to think about how to have the guesser interact with a human setter, so that it can play against a human. It won't get much use, but we should have it for completeness.\n",
19 "\n",
20 "But before we can build something that fits the hole, we need to understand what shape the hole is. What is the interface between the guesser and the setter?\n",
21 "\n",
22 "Spoiler space\n",
23 "\n",
24 ".\n",
25 "\n",
26 ".\n",
27 "\n",
28 ".\n",
29 "\n",
30 ".\n",
31 "\n",
32 ".\n",
33 "\n",
34 ".\n",
35 "\n",
36 "."
37 ]
38 },
39 {
40 "cell_type": "markdown",
41 "metadata": {},
42 "source": [
43 "The guesser only really needs to do one thing: make a guess. We'll say that the setter passes in the complete state of the game (disovered template, lives remaining, and wrong letters guessed) and the guesser simply returns the guess.\n",
44 "\n",
45 "Someone needs to keep track of the incorrect guesses so that the guesser doesn't keep making the same wrong guess. As the setter is already keeping track of the rest of the state, it's probably easier if the setter also keeps track of the wrong letters. That might allow us to keep the guesser state-free, which will keep it simpler. "
46 ]
47 },
48 {
49 "cell_type": "markdown",
50 "metadata": {},
51 "source": [
52 "## Getting a game state\n",
53 "If we need the guesser to interact with a human, let's get it to read the game state from a human.\n",
54 "\n",
55 "Remember that we want to return two lists of characters, not two strings.\n",
56 "\n",
57 "We'll also not do any input validation. Life's too short (for this example)."
58 ]
59 },
60 {
61 "cell_type": "code",
62 "collapsed": false,
63 "input": [
64 "def read_game():\n",
65 " discovered = input('Enter the discovered word: ')\n",
66 " missed = input('Enter the wrong guesses: ')\n",
67 " return list(discovered), list(missed)"
68 ],
69 "language": "python",
70 "metadata": {},
71 "outputs": [],
72 "prompt_number": 18
73 },
74 {
75 "cell_type": "code",
76 "collapsed": false,
77 "input": [
78 "read_game()"
79 ],
80 "language": "python",
81 "metadata": {},
82 "outputs": [
83 {
84 "name": "stdout",
85 "output_type": "stream",
86 "stream": "stdout",
87 "text": [
88 "Enter the discovered word: __pp_\n"
89 ]
90 },
91 {
92 "name": "stdout",
93 "output_type": "stream",
94 "stream": "stdout",
95 "text": [
96 "Enter the wrong guesses: xv\n"
97 ]
98 },
99 {
100 "metadata": {},
101 "output_type": "pyout",
102 "prompt_number": 19,
103 "text": [
104 "(['_', '_', 'p', 'p', '_'], ['x', 'v'])"
105 ]
106 }
107 ],
108 "prompt_number": 19
109 },
110 {
111 "cell_type": "markdown",
112 "metadata": {},
113 "source": [
114 "That was easy."
115 ]
116 },
117 {
118 "cell_type": "markdown",
119 "metadata": {},
120 "source": [
121 "## Basic strategy\n",
122 "\n",
123 "Our first strategy is to simply guess the letters in order, with \"order\" defined as how often the letters appear in English running text. That means we need to read some English text and count the letters."
124 ]
125 },
126 {
127 "cell_type": "code",
128 "collapsed": false,
129 "input": [
130 "import string\n",
131 "import collections"
132 ],
133 "language": "python",
134 "metadata": {},
135 "outputs": [],
136 "prompt_number": 20
137 },
138 {
139 "cell_type": "markdown",
140 "metadata": {},
141 "source": [
142 "The `collections` standard library contains all sorts of fun stuff. The `Counter` object is very useful. Take a look at the documentation for what it does. We'll be using two features here: generate a `dict`-like thing of counts, and return the things in frequency order.\n",
143 "\n",
144 "First, let's read all the letters in *The Complete Works of Sherlock Holmes*."
145 ]
146 },
147 {
148 "cell_type": "code",
149 "collapsed": false,
150 "input": [
151 "letter_counts = collections.Counter(l.lower() for l in open('../sherlock-holmes.txt').read() if l in string.ascii_letters)\n",
152 "letter_counts"
153 ],
154 "language": "python",
155 "metadata": {},
156 "outputs": [
157 {
158 "metadata": {},
159 "output_type": "pyout",
160 "prompt_number": 21,
161 "text": [
162 "Counter({'e': 53111, 't': 38981, 'a': 35137, 'o': 33512, 'i': 30140, 'h': 29047, 'n': 28682, 's': 27194, 'r': 24508, 'd': 18563, 'l': 17145, 'u': 13116, 'm': 11787, 'w': 11266, 'c': 10499, 'y': 9431, 'f': 8975, 'g': 7887, 'p': 6835, 'b': 6362, 'v': 4452, 'k': 3543, 'x': 549, 'j': 452, 'q': 426, 'z': 149})"
163 ]
164 }
165 ],
166 "prompt_number": 21
167 },
168 {
169 "cell_type": "markdown",
170 "metadata": {},
171 "source": [
172 "Now, let's get them listed in order."
173 ]
174 },
175 {
176 "cell_type": "code",
177 "collapsed": false,
178 "input": [
179 "letter_counts.most_common()"
180 ],
181 "language": "python",
182 "metadata": {},
183 "outputs": [
184 {
185 "metadata": {},
186 "output_type": "pyout",
187 "prompt_number": 22,
188 "text": [
189 "[('e', 53111),\n",
190 " ('t', 38981),\n",
191 " ('a', 35137),\n",
192 " ('o', 33512),\n",
193 " ('i', 30140),\n",
194 " ('h', 29047),\n",
195 " ('n', 28682),\n",
196 " ('s', 27194),\n",
197 " ('r', 24508),\n",
198 " ('d', 18563),\n",
199 " ('l', 17145),\n",
200 " ('u', 13116),\n",
201 " ('m', 11787),\n",
202 " ('w', 11266),\n",
203 " ('c', 10499),\n",
204 " ('y', 9431),\n",
205 " ('f', 8975),\n",
206 " ('g', 7887),\n",
207 " ('p', 6835),\n",
208 " ('b', 6362),\n",
209 " ('v', 4452),\n",
210 " ('k', 3543),\n",
211 " ('x', 549),\n",
212 " ('j', 452),\n",
213 " ('q', 426),\n",
214 " ('z', 149)]"
215 ]
216 }
217 ],
218 "prompt_number": 22
219 },
220 {
221 "cell_type": "code",
222 "collapsed": false,
223 "input": [
224 "letters_in_order = [p[0] for p in letter_counts.most_common()]\n",
225 "letters_in_order"
226 ],
227 "language": "python",
228 "metadata": {},
229 "outputs": [
230 {
231 "metadata": {},
232 "output_type": "pyout",
233 "prompt_number": 23,
234 "text": [
235 "['e',\n",
236 " 't',\n",
237 " 'a',\n",
238 " 'o',\n",
239 " 'i',\n",
240 " 'h',\n",
241 " 'n',\n",
242 " 's',\n",
243 " 'r',\n",
244 " 'd',\n",
245 " 'l',\n",
246 " 'u',\n",
247 " 'm',\n",
248 " 'w',\n",
249 " 'c',\n",
250 " 'y',\n",
251 " 'f',\n",
252 " 'g',\n",
253 " 'p',\n",
254 " 'b',\n",
255 " 'v',\n",
256 " 'k',\n",
257 " 'x',\n",
258 " 'j',\n",
259 " 'q',\n",
260 " 'z']"
261 ]
262 }
263 ],
264 "prompt_number": 23
265 },
266 {
267 "cell_type": "markdown",
268 "metadata": {},
269 "source": [
270 "## Make a guess\n",
271 "How to make a guess? We want to guess the most common letter that hasn't already been guessed. \n",
272 "\n",
273 "That means we want to filter the available letters by removing the ones already guessed, and then take the first letter of the ones that are left. "
274 ]
275 },
276 {
277 "cell_type": "code",
278 "collapsed": false,
279 "input": [
280 "def make_guess(discovered, missed):\n",
281 " return [l for l in letters_in_order if l not in (discovered + missed)][0]"
282 ],
283 "language": "python",
284 "metadata": {},
285 "outputs": [],
286 "prompt_number": 27
287 },
288 {
289 "cell_type": "code",
290 "collapsed": false,
291 "input": [
292 "make_guess(list('__pp_'), list('exv'))"
293 ],
294 "language": "python",
295 "metadata": {},
296 "outputs": [
297 {
298 "metadata": {},
299 "output_type": "pyout",
300 "prompt_number": 26,
301 "text": [
302 "'t'"
303 ]
304 }
305 ],
306 "prompt_number": 26
307 },
308 {
309 "cell_type": "markdown",
310 "metadata": {},
311 "source": [
312 "...and that's it. We don't need to keep track of game end: the setting player can do that.\n",
313 "\n",
314 "It's easy to see how we could have different strategies, depending on the order of letters used in the `make_guess` procedure. But that's for next time."
315 ]
316 },
317 {
318 "cell_type": "code",
319 "collapsed": false,
320 "input": [],
321 "language": "python",
322 "metadata": {},
323 "outputs": []
324 }
325 ],
326 "metadata": {}
327 }
328 ]
329 }