Changing machine
[cas-master-teacher-training.git] / hangman / 02-hangman-guesser.ipynb
1 {
2 "metadata": {
3 "name": "",
4 "signature": "sha256:393162de43635426518dd3dca8100dca49558a55f990aede7c52f3a98de60612"
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 "## Basic strategy\n",
53 "\n",
54 "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."
55 ]
56 },
57 {
58 "cell_type": "code",
59 "collapsed": false,
60 "input": [
61 "import string\n",
62 "import collections"
63 ],
64 "language": "python",
65 "metadata": {},
66 "outputs": [],
67 "prompt_number": 1
68 },
69 {
70 "cell_type": "markdown",
71 "metadata": {},
72 "source": [
73 "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",
74 "\n",
75 "First, let's read all the letters in *The Complete Works of Sherlock Holmes*."
76 ]
77 },
78 {
79 "cell_type": "code",
80 "collapsed": false,
81 "input": [
82 "letter_counts = collections.Counter(l.lower() for l in open('../sherlock-holmes.txt').read() if l in string.ascii_letters)\n",
83 "letter_counts"
84 ],
85 "language": "python",
86 "metadata": {},
87 "outputs": [
88 {
89 "metadata": {},
90 "output_type": "pyout",
91 "prompt_number": 3,
92 "text": [
93 "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})"
94 ]
95 }
96 ],
97 "prompt_number": 3
98 },
99 {
100 "cell_type": "markdown",
101 "metadata": {},
102 "source": [
103 "Now, let's get them listed in order."
104 ]
105 },
106 {
107 "cell_type": "code",
108 "collapsed": false,
109 "input": [
110 "letter_counts.most_common()"
111 ],
112 "language": "python",
113 "metadata": {},
114 "outputs": [
115 {
116 "metadata": {},
117 "output_type": "pyout",
118 "prompt_number": 6,
119 "text": [
120 "[('e', 53111),\n",
121 " ('t', 38981),\n",
122 " ('a', 35137),\n",
123 " ('o', 33512),\n",
124 " ('i', 30140),\n",
125 " ('h', 29047),\n",
126 " ('n', 28682),\n",
127 " ('s', 27194),\n",
128 " ('r', 24508),\n",
129 " ('d', 18563),\n",
130 " ('l', 17145),\n",
131 " ('u', 13116),\n",
132 " ('m', 11787),\n",
133 " ('w', 11266),\n",
134 " ('c', 10499),\n",
135 " ('y', 9431),\n",
136 " ('f', 8975),\n",
137 " ('g', 7887),\n",
138 " ('p', 6835),\n",
139 " ('b', 6362),\n",
140 " ('v', 4452),\n",
141 " ('k', 3543),\n",
142 " ('x', 549),\n",
143 " ('j', 452),\n",
144 " ('q', 426),\n",
145 " ('z', 149)]"
146 ]
147 }
148 ],
149 "prompt_number": 6
150 },
151 {
152 "cell_type": "code",
153 "collapsed": false,
154 "input": [
155 "letters_in_order = [p[0] for p in letter_counts.most_common()]\n",
156 "letters_in_order"
157 ],
158 "language": "python",
159 "metadata": {},
160 "outputs": [
161 {
162 "metadata": {},
163 "output_type": "pyout",
164 "prompt_number": 4,
165 "text": [
166 "['e',\n",
167 " 't',\n",
168 " 'a',\n",
169 " 'o',\n",
170 " 'i',\n",
171 " 'h',\n",
172 " 'n',\n",
173 " 's',\n",
174 " 'r',\n",
175 " 'd',\n",
176 " 'l',\n",
177 " 'u',\n",
178 " 'm',\n",
179 " 'w',\n",
180 " 'c',\n",
181 " 'y',\n",
182 " 'f',\n",
183 " 'g',\n",
184 " 'p',\n",
185 " 'b',\n",
186 " 'v',\n",
187 " 'k',\n",
188 " 'x',\n",
189 " 'j',\n",
190 " 'q',\n",
191 " 'z']"
192 ]
193 }
194 ],
195 "prompt_number": 4
196 },
197 {
198 "cell_type": "code",
199 "collapsed": false,
200 "input": [
201 "def make_guess():\n",
202 " guessed_letters = read_game()\n",
203 " unguessed_letters_in_order = ordered_subtract(letters_in_order, guessed_letters)\n",
204 " print('My guess is:', unguessed_letters_in_order[0])"
205 ],
206 "language": "python",
207 "metadata": {},
208 "outputs": [],
209 "prompt_number": 29
210 },
211 {
212 "cell_type": "code",
213 "collapsed": false,
214 "input": [
215 "def read_game():\n",
216 " discovered = input('Enter the discovered word: ')\n",
217 " missed = input('Enter the wrong guesses: ')\n",
218 " return [l for l in lower(discovered + missed) if l in string.ascii_letters]"
219 ],
220 "language": "python",
221 "metadata": {},
222 "outputs": [],
223 "prompt_number": 30
224 },
225 {
226 "cell_type": "code",
227 "collapsed": false,
228 "input": [
229 "def ordered_subtract(ordered_list, to_remove):\n",
230 " for r in to_remove:\n",
231 " if r in ordered_list:\n",
232 " ordered_list.remove(r)\n",
233 " return ordered_list"
234 ],
235 "language": "python",
236 "metadata": {},
237 "outputs": [],
238 "prompt_number": 19
239 },
240 {
241 "cell_type": "code",
242 "collapsed": false,
243 "input": [
244 "letters_in_order"
245 ],
246 "language": "python",
247 "metadata": {},
248 "outputs": [
249 {
250 "metadata": {},
251 "output_type": "pyout",
252 "prompt_number": 20,
253 "text": [
254 "['e',\n",
255 " 't',\n",
256 " 'a',\n",
257 " 'o',\n",
258 " 'i',\n",
259 " 'h',\n",
260 " 'n',\n",
261 " 's',\n",
262 " 'r',\n",
263 " 'd',\n",
264 " 'l',\n",
265 " 'u',\n",
266 " 'm',\n",
267 " 'w',\n",
268 " 'c',\n",
269 " 'y',\n",
270 " 'f',\n",
271 " 'g',\n",
272 " 'p',\n",
273 " 'b',\n",
274 " 'v',\n",
275 " 'k',\n",
276 " 'x',\n",
277 " 'j',\n",
278 " 'q',\n",
279 " 'z']"
280 ]
281 }
282 ],
283 "prompt_number": 20
284 },
285 {
286 "cell_type": "code",
287 "collapsed": false,
288 "input": [
289 "ordered_subtract(letters_in_order, 'etaoin')"
290 ],
291 "language": "python",
292 "metadata": {},
293 "outputs": [
294 {
295 "metadata": {},
296 "output_type": "pyout",
297 "prompt_number": 21,
298 "text": [
299 "['h',\n",
300 " 's',\n",
301 " 'r',\n",
302 " 'd',\n",
303 " 'l',\n",
304 " 'u',\n",
305 " 'm',\n",
306 " 'w',\n",
307 " 'c',\n",
308 " 'y',\n",
309 " 'f',\n",
310 " 'g',\n",
311 " 'p',\n",
312 " 'b',\n",
313 " 'v',\n",
314 " 'k',\n",
315 " 'x',\n",
316 " 'j',\n",
317 " 'q',\n",
318 " 'z']"
319 ]
320 }
321 ],
322 "prompt_number": 21
323 },
324 {
325 "cell_type": "code",
326 "collapsed": false,
327 "input": [
328 "letters_in_order"
329 ],
330 "language": "python",
331 "metadata": {},
332 "outputs": [
333 {
334 "metadata": {},
335 "output_type": "pyout",
336 "prompt_number": 22,
337 "text": [
338 "['h',\n",
339 " 's',\n",
340 " 'r',\n",
341 " 'd',\n",
342 " 'l',\n",
343 " 'u',\n",
344 " 'm',\n",
345 " 'w',\n",
346 " 'c',\n",
347 " 'y',\n",
348 " 'f',\n",
349 " 'g',\n",
350 " 'p',\n",
351 " 'b',\n",
352 " 'v',\n",
353 " 'k',\n",
354 " 'x',\n",
355 " 'j',\n",
356 " 'q',\n",
357 " 'z']"
358 ]
359 }
360 ],
361 "prompt_number": 22
362 },
363 {
364 "cell_type": "code",
365 "collapsed": false,
366 "input": [
367 "def ordered_subtract(ordered_list, to_remove):\n",
368 " for r in to_remove:\n",
369 " if r in ordered_list:\n",
370 " ri = ordered_list.index(r)\n",
371 " ordered_list = ordered_list[:ri] + ordered_list[ri+1:]\n",
372 " return ordered_list"
373 ],
374 "language": "python",
375 "metadata": {},
376 "outputs": [],
377 "prompt_number": 26
378 },
379 {
380 "cell_type": "code",
381 "collapsed": false,
382 "input": [
383 "letters_in_order = [p[0] for p in letter_counts.most_common()]\n",
384 "letters_in_order"
385 ],
386 "language": "python",
387 "metadata": {},
388 "outputs": [
389 {
390 "metadata": {},
391 "output_type": "pyout",
392 "prompt_number": 24,
393 "text": [
394 "['e',\n",
395 " 't',\n",
396 " 'a',\n",
397 " 'o',\n",
398 " 'i',\n",
399 " 'h',\n",
400 " 'n',\n",
401 " 's',\n",
402 " 'r',\n",
403 " 'd',\n",
404 " 'l',\n",
405 " 'u',\n",
406 " 'm',\n",
407 " 'w',\n",
408 " 'c',\n",
409 " 'y',\n",
410 " 'f',\n",
411 " 'g',\n",
412 " 'p',\n",
413 " 'b',\n",
414 " 'v',\n",
415 " 'k',\n",
416 " 'x',\n",
417 " 'j',\n",
418 " 'q',\n",
419 " 'z']"
420 ]
421 }
422 ],
423 "prompt_number": 24
424 },
425 {
426 "cell_type": "code",
427 "collapsed": false,
428 "input": [
429 "ordered_subtract(letters_in_order, 'etaoin')"
430 ],
431 "language": "python",
432 "metadata": {},
433 "outputs": [
434 {
435 "metadata": {},
436 "output_type": "pyout",
437 "prompt_number": 27,
438 "text": [
439 "['h',\n",
440 " 's',\n",
441 " 'r',\n",
442 " 'd',\n",
443 " 'l',\n",
444 " 'u',\n",
445 " 'm',\n",
446 " 'w',\n",
447 " 'c',\n",
448 " 'y',\n",
449 " 'f',\n",
450 " 'g',\n",
451 " 'p',\n",
452 " 'b',\n",
453 " 'v',\n",
454 " 'k',\n",
455 " 'x',\n",
456 " 'j',\n",
457 " 'q',\n",
458 " 'z']"
459 ]
460 }
461 ],
462 "prompt_number": 27
463 },
464 {
465 "cell_type": "code",
466 "collapsed": false,
467 "input": [
468 "letters_in_order"
469 ],
470 "language": "python",
471 "metadata": {},
472 "outputs": [
473 {
474 "metadata": {},
475 "output_type": "pyout",
476 "prompt_number": 28,
477 "text": [
478 "['e',\n",
479 " 't',\n",
480 " 'a',\n",
481 " 'o',\n",
482 " 'i',\n",
483 " 'h',\n",
484 " 'n',\n",
485 " 's',\n",
486 " 'r',\n",
487 " 'd',\n",
488 " 'l',\n",
489 " 'u',\n",
490 " 'm',\n",
491 " 'w',\n",
492 " 'c',\n",
493 " 'y',\n",
494 " 'f',\n",
495 " 'g',\n",
496 " 'p',\n",
497 " 'b',\n",
498 " 'v',\n",
499 " 'k',\n",
500 " 'x',\n",
501 " 'j',\n",
502 " 'q',\n",
503 " 'z']"
504 ]
505 }
506 ],
507 "prompt_number": 28
508 },
509 {
510 "cell_type": "code",
511 "collapsed": false,
512 "input": [
513 "make_guess()"
514 ],
515 "language": "python",
516 "metadata": {},
517 "outputs": [
518 {
519 "name": "stdout",
520 "output_type": "stream",
521 "stream": "stdout",
522 "text": [
523 "Enter the discovered word: _a__y\n"
524 ]
525 },
526 {
527 "name": "stdout",
528 "output_type": "stream",
529 "stream": "stdout",
530 "text": [
531 "Enter the wrong guesses: eit\n"
532 ]
533 },
534 {
535 "output_type": "stream",
536 "stream": "stdout",
537 "text": [
538 "My guess is: o\n"
539 ]
540 }
541 ],
542 "prompt_number": 31
543 },
544 {
545 "cell_type": "code",
546 "collapsed": false,
547 "input": [],
548 "language": "python",
549 "metadata": {},
550 "outputs": []
551 }
552 ],
553 "metadata": {}
554 }
555 ]
556 }