Updated notebook versions
[cas-master-teacher-training.git] / hangman / 03-hangman-both.ipynb
1 {
2 "cells": [
3 {
4 "cell_type": "markdown",
5 "metadata": {},
6 "source": [
7 "# Putting both halves together\n",
8 "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",
9 "\n",
10 "The word of this section is \"encapsulation.\" \n",
11 "\n",
12 "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",
13 "\n",
14 "Back to encapsulation.\n",
15 "\n",
16 "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",
17 "\n",
18 "In this case, objects seem like a natural fit.\n",
19 "\n",
20 "(Insert Neil's aikido instructor story here.)"
21 ]
22 },
23 {
24 "cell_type": "markdown",
25 "metadata": {},
26 "source": [
27 "## Object-orientation in Python\n",
28 "There are a few things to remember about object orientation in Python.\n",
29 "\n",
30 "1. It's an ugly kludge that was bolted on long after the language was defined.\n",
31 "\n",
32 "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",
33 "\n",
34 "3. Use `self.whatever` to refer to the instance variable `whatever`\n",
35 "\n",
36 "4. Use `self.however(arg1, arg2)` when one method calls another in the same object.\n",
37 "\n",
38 "Apart from that, it's pretty straightforward."
39 ]
40 },
41 {
42 "cell_type": "code",
43 "execution_count": 43,
44 "metadata": {
45 "collapsed": false
46 },
47 "outputs": [],
48 "source": [
49 "import re\n",
50 "import random\n",
51 "import string\n",
52 "import collections"
53 ]
54 },
55 {
56 "cell_type": "code",
57 "execution_count": 44,
58 "metadata": {
59 "collapsed": false
60 },
61 "outputs": [],
62 "source": [
63 "WORDS = [w.strip() for w in open('/usr/share/dict/british-english').readlines() \n",
64 " if re.match(r'^[a-z]*$', w.strip())]"
65 ]
66 },
67 {
68 "cell_type": "code",
69 "execution_count": 45,
70 "metadata": {
71 "collapsed": false
72 },
73 "outputs": [],
74 "source": [
75 "STARTING_LIVES = 10"
76 ]
77 },
78 {
79 "cell_type": "markdown",
80 "metadata": {},
81 "source": [
82 "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",
83 "\n",
84 "* `initialise()` is now named `__init__()` (note the double underscores fore and aft).\n",
85 "* `player` and `lives` are parameters to the `game`, but with default values.\n",
86 "* I've added two new flags, `game_won` and `game_lost`.\n",
87 "* `do_turn` asks the player for a guess, if there is a player.\n",
88 "* 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",
89 "* 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",
90 "* `report_on_game()` has been added for when the game is being played against a human."
91 ]
92 },
93 {
94 "cell_type": "code",
95 "execution_count": 46,
96 "metadata": {
97 "collapsed": false
98 },
99 "outputs": [],
100 "source": [
101 "class Game:\n",
102 " def __init__(self, target, player=None, lives=STARTING_LIVES):\n",
103 " self.lives = lives\n",
104 " self.player = player\n",
105 " self.target = target\n",
106 " self.discovered = list('_' * len(target))\n",
107 " self.wrong_letters = []\n",
108 " self.game_finished = False\n",
109 " self.game_won = False\n",
110 " self.game_lost = False\n",
111 " \n",
112 " def find_all(self, letter):\n",
113 " return [p for p, l in enumerate(self.target) if l == letter]\n",
114 " \n",
115 " def update_discovered_word(self, guessed_letter):\n",
116 " locations = self.find_all(guessed_letter)\n",
117 " for location in locations:\n",
118 " self.discovered[location] = guessed_letter\n",
119 " return self.discovered\n",
120 " \n",
121 " def do_turn(self):\n",
122 " if self.player:\n",
123 " guess = self.player.guess(self.discovered, self.wrong_letters, self.lives)\n",
124 " else:\n",
125 " guess = self.ask_for_guess()\n",
126 " if guess in self.target:\n",
127 " self.update_discovered_word(guess)\n",
128 " else:\n",
129 " self.lives -= 1\n",
130 " if guess not in self.wrong_letters:\n",
131 " self.wrong_letters += [guess]\n",
132 " if self.lives == 0:\n",
133 " self.game_finished = True\n",
134 " self.game_lost = True\n",
135 " if '_' not in self.discovered:\n",
136 " self.game_finished = True\n",
137 " self.game_won = True\n",
138 " \n",
139 " def ask_for_guess(self):\n",
140 " print('Word:', ' '.join(self.discovered), \n",
141 " ' : Lives =', self.lives, \n",
142 " ', wrong guesses:', ' '.join(sorted(self.wrong_letters)))\n",
143 " guess = input('Enter letter: ').strip().lower()[0]\n",
144 " return guess\n",
145 " \n",
146 " def play_game(self):\n",
147 " self.do_turn()\n",
148 " while not self.game_finished:\n",
149 " self.do_turn()\n",
150 " if not self.player:\n",
151 " self.report_on_game()\n",
152 " return self.game_won\n",
153 " \n",
154 " def report_on_game(self):\n",
155 " if self.game_won:\n",
156 " print('You won! The word was', self.target)\n",
157 " else:\n",
158 " print('You lost. The word was', self.target)\n",
159 " return self.game_won"
160 ]
161 },
162 {
163 "cell_type": "markdown",
164 "metadata": {},
165 "source": [
166 "Let's test it!"
167 ]
168 },
169 {
170 "cell_type": "code",
171 "execution_count": 47,
172 "metadata": {
173 "collapsed": false
174 },
175 "outputs": [],
176 "source": [
177 "g = Game(random.choice(WORDS))"
178 ]
179 },
180 {
181 "cell_type": "code",
182 "execution_count": 48,
183 "metadata": {
184 "collapsed": false
185 },
186 "outputs": [
187 {
188 "data": {
189 "text/plain": [
190 "'distension'"
191 ]
192 },
193 "execution_count": 48,
194 "metadata": {},
195 "output_type": "execute_result"
196 }
197 ],
198 "source": [
199 "g.target"
200 ]
201 },
202 {
203 "cell_type": "code",
204 "execution_count": 49,
205 "metadata": {
206 "collapsed": false
207 },
208 "outputs": [
209 {
210 "data": {
211 "text/plain": [
212 "['_', '_', '_', '_', '_', '_', '_', '_', '_', '_']"
213 ]
214 },
215 "execution_count": 49,
216 "metadata": {},
217 "output_type": "execute_result"
218 }
219 ],
220 "source": [
221 "g.discovered"
222 ]
223 },
224 {
225 "cell_type": "code",
226 "execution_count": 50,
227 "metadata": {
228 "collapsed": false
229 },
230 "outputs": [
231 {
232 "name": "stdout",
233 "output_type": "stream",
234 "text": [
235 "Word: _ _ _ _ _ _ _ _ _ _ : Lives = 10 , wrong guesses: \n",
236 "Enter letter: e\n"
237 ]
238 }
239 ],
240 "source": [
241 "g.do_turn()"
242 ]
243 },
244 {
245 "cell_type": "code",
246 "execution_count": 51,
247 "metadata": {
248 "collapsed": false
249 },
250 "outputs": [
251 {
252 "data": {
253 "text/plain": [
254 "10"
255 ]
256 },
257 "execution_count": 51,
258 "metadata": {},
259 "output_type": "execute_result"
260 }
261 ],
262 "source": [
263 "g.lives"
264 ]
265 },
266 {
267 "cell_type": "code",
268 "execution_count": 52,
269 "metadata": {
270 "collapsed": false
271 },
272 "outputs": [
273 {
274 "data": {
275 "text/plain": [
276 "[]"
277 ]
278 },
279 "execution_count": 52,
280 "metadata": {},
281 "output_type": "execute_result"
282 }
283 ],
284 "source": [
285 "g.wrong_letters"
286 ]
287 },
288 {
289 "cell_type": "code",
290 "execution_count": 53,
291 "metadata": {
292 "collapsed": false
293 },
294 "outputs": [
295 {
296 "name": "stdout",
297 "output_type": "stream",
298 "text": [
299 "Word: _ _ _ _ e _ _ _ _ _ : Lives = 10 , wrong guesses: \n",
300 "Enter letter: q\n"
301 ]
302 }
303 ],
304 "source": [
305 "g.do_turn()"
306 ]
307 },
308 {
309 "cell_type": "code",
310 "execution_count": 54,
311 "metadata": {
312 "collapsed": false
313 },
314 "outputs": [
315 {
316 "data": {
317 "text/plain": [
318 "9"
319 ]
320 },
321 "execution_count": 54,
322 "metadata": {},
323 "output_type": "execute_result"
324 }
325 ],
326 "source": [
327 "g.lives"
328 ]
329 },
330 {
331 "cell_type": "code",
332 "execution_count": 55,
333 "metadata": {
334 "collapsed": false
335 },
336 "outputs": [
337 {
338 "data": {
339 "text/plain": [
340 "['_', '_', '_', '_', 'e', '_', '_', '_', '_', '_']"
341 ]
342 },
343 "execution_count": 55,
344 "metadata": {},
345 "output_type": "execute_result"
346 }
347 ],
348 "source": [
349 "g.discovered"
350 ]
351 },
352 {
353 "cell_type": "code",
354 "execution_count": 56,
355 "metadata": {
356 "collapsed": false
357 },
358 "outputs": [
359 {
360 "data": {
361 "text/plain": [
362 "['q']"
363 ]
364 },
365 "execution_count": 56,
366 "metadata": {},
367 "output_type": "execute_result"
368 }
369 ],
370 "source": [
371 "g.wrong_letters"
372 ]
373 },
374 {
375 "cell_type": "code",
376 "execution_count": 57,
377 "metadata": {
378 "collapsed": false
379 },
380 "outputs": [
381 {
382 "name": "stdout",
383 "output_type": "stream",
384 "text": [
385 "Word: _ _ _ _ _ _ _ _ : Lives = 10 , wrong guesses: \n",
386 "Enter letter: e\n",
387 "Word: e _ e _ _ _ _ e : Lives = 10 , wrong guesses: \n",
388 "Enter letter: x\n",
389 "Word: e _ e _ _ _ _ e : Lives = 9 , wrong guesses: x\n",
390 "Enter letter: s\n",
391 "Word: e _ e _ _ _ _ e : Lives = 8 , wrong guesses: s x\n",
392 "Enter letter: l\n",
393 "Word: e _ e _ _ _ _ e : Lives = 7 , wrong guesses: l s x\n",
394 "Enter letter: t\n",
395 "Word: e _ e _ _ _ _ e : Lives = 6 , wrong guesses: l s t x\n",
396 "Enter letter: m\n",
397 "Word: e _ e _ _ _ _ e : Lives = 5 , wrong guesses: l m s t x\n",
398 "Enter letter: h\n",
399 "Word: e _ e _ _ _ _ e : Lives = 4 , wrong guesses: h l m s t x\n",
400 "Enter letter: i\n",
401 "Word: e _ e _ _ _ _ e : Lives = 3 , wrong guesses: h i l m s t x\n",
402 "Enter letter: a\n",
403 "Word: e _ e _ _ _ _ e : Lives = 2 , wrong guesses: a h i l m s t x\n",
404 "Enter letter: o\n",
405 "Word: e _ e _ _ o _ e : Lives = 2 , wrong guesses: a h i l m s t x\n",
406 "Enter letter: n\n",
407 "Word: e _ e _ _ o n e : Lives = 2 , wrong guesses: a h i l m s t x\n",
408 "Enter letter: r\n",
409 "Word: e _ e r _ o n e : Lives = 2 , wrong guesses: a h i l m s t x\n",
410 "Enter letter: v\n",
411 "Word: e v e r _ o n e : Lives = 2 , wrong guesses: a h i l m s t x\n",
412 "Enter letter: y\n",
413 "You won! The word was everyone\n"
414 ]
415 },
416 {
417 "data": {
418 "text/plain": [
419 "True"
420 ]
421 },
422 "execution_count": 57,
423 "metadata": {},
424 "output_type": "execute_result"
425 }
426 ],
427 "source": [
428 "g = Game(random.choice(WORDS))\n",
429 "g.play_game()"
430 ]
431 },
432 {
433 "cell_type": "code",
434 "execution_count": 58,
435 "metadata": {
436 "collapsed": false
437 },
438 "outputs": [
439 {
440 "name": "stdout",
441 "output_type": "stream",
442 "text": [
443 "Word: _ _ _ _ _ _ _ _ _ : Lives = 10 , wrong guesses: \n",
444 "Enter letter: q\n",
445 "Word: _ _ _ _ _ _ _ _ _ : Lives = 9 , wrong guesses: q\n",
446 "Enter letter: z\n",
447 "Word: _ _ _ _ _ _ _ _ _ : Lives = 8 , wrong guesses: q z\n",
448 "Enter letter: x\n",
449 "Word: _ _ _ _ _ _ _ _ _ : Lives = 7 , wrong guesses: q x z\n",
450 "Enter letter: e\n",
451 "Word: _ _ _ _ _ _ _ _ _ : Lives = 6 , wrong guesses: e q x z\n",
452 "Enter letter: a\n",
453 "Word: _ _ a _ _ _ _ _ _ : Lives = 6 , wrong guesses: e q x z\n",
454 "Enter letter: v\n",
455 "Word: _ _ a _ _ _ _ _ _ : Lives = 5 , wrong guesses: e q v x z\n",
456 "Enter letter: b\n",
457 "Word: _ _ a _ _ _ _ _ _ : Lives = 4 , wrong guesses: b e q v x z\n",
458 "Enter letter: k\n",
459 "Word: _ _ a _ k _ k _ _ : Lives = 4 , wrong guesses: b e q v x z\n",
460 "Enter letter: l\n",
461 "Word: _ _ a _ k _ k _ _ : Lives = 3 , wrong guesses: b e l q v x z\n",
462 "Enter letter: j\n",
463 "Word: _ _ a _ k _ k _ _ : Lives = 2 , wrong guesses: b e j l q v x z\n",
464 "Enter letter: p\n",
465 "Word: _ _ a _ k _ k _ _ : Lives = 1 , wrong guesses: b e j l p q v x z\n",
466 "Enter letter: d\n",
467 "You lost. The word was sharkskin\n"
468 ]
469 },
470 {
471 "data": {
472 "text/plain": [
473 "False"
474 ]
475 },
476 "execution_count": 58,
477 "metadata": {},
478 "output_type": "execute_result"
479 }
480 ],
481 "source": [
482 "g = Game(random.choice(WORDS))\n",
483 "g.play_game()"
484 ]
485 },
486 {
487 "cell_type": "code",
488 "execution_count": 59,
489 "metadata": {
490 "collapsed": false
491 },
492 "outputs": [
493 {
494 "data": {
495 "text/plain": [
496 "'sharkskin'"
497 ]
498 },
499 "execution_count": 59,
500 "metadata": {},
501 "output_type": "execute_result"
502 }
503 ],
504 "source": [
505 "g.target"
506 ]
507 },
508 {
509 "cell_type": "markdown",
510 "metadata": {},
511 "source": [
512 "## Player objects\n",
513 "Let's work ourselves gently into this.\n",
514 "\n",
515 "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."
516 ]
517 },
518 {
519 "cell_type": "code",
520 "execution_count": 60,
521 "metadata": {
522 "collapsed": false
523 },
524 "outputs": [],
525 "source": [
526 "class PlayerAlphabetical:\n",
527 " def guess(self, discovered, missed, lives):\n",
528 " return [l for l in string.ascii_lowercase if l not in discovered + missed][0]"
529 ]
530 },
531 {
532 "cell_type": "markdown",
533 "metadata": {},
534 "source": [
535 "That wasn't too hard, was it?\n",
536 "\n",
537 "Let's use it to play a game."
538 ]
539 },
540 {
541 "cell_type": "code",
542 "execution_count": 69,
543 "metadata": {
544 "collapsed": false
545 },
546 "outputs": [],
547 "source": [
548 "g = Game(random.choice(WORDS), player=PlayerAlphabetical())"
549 ]
550 },
551 {
552 "cell_type": "code",
553 "execution_count": 70,
554 "metadata": {
555 "collapsed": false
556 },
557 "outputs": [
558 {
559 "data": {
560 "text/plain": [
561 "False"
562 ]
563 },
564 "execution_count": 70,
565 "metadata": {},
566 "output_type": "execute_result"
567 }
568 ],
569 "source": [
570 "g.play_game()"
571 ]
572 },
573 {
574 "cell_type": "code",
575 "execution_count": 71,
576 "metadata": {
577 "collapsed": false
578 },
579 "outputs": [
580 {
581 "data": {
582 "text/plain": [
583 "['c', 'e', '_', '_', 'i', 'f', 'i', 'c', 'a', '_', 'e', '_']"
584 ]
585 },
586 "execution_count": 71,
587 "metadata": {},
588 "output_type": "execute_result"
589 }
590 ],
591 "source": [
592 "g.discovered"
593 ]
594 },
595 {
596 "cell_type": "code",
597 "execution_count": 72,
598 "metadata": {
599 "collapsed": false
600 },
601 "outputs": [
602 {
603 "data": {
604 "text/plain": [
605 "'certificates'"
606 ]
607 },
608 "execution_count": 72,
609 "metadata": {},
610 "output_type": "execute_result"
611 }
612 ],
613 "source": [
614 "g.target"
615 ]
616 },
617 {
618 "cell_type": "markdown",
619 "metadata": {},
620 "source": [
621 "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."
622 ]
623 },
624 {
625 "cell_type": "code",
626 "execution_count": 74,
627 "metadata": {
628 "collapsed": false
629 },
630 "outputs": [
631 {
632 "name": "stdout",
633 "output_type": "stream",
634 "text": [
635 "Target: turnoffs ; Discovered: ['_', '_', '_', '_', '_', 'f', 'f', '_'] ; Lives remaining: 0\n"
636 ]
637 }
638 ],
639 "source": [
640 "g = Game(random.choice(WORDS), player=PlayerAlphabetical())\n",
641 "g.play_game()\n",
642 "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
643 ]
644 },
645 {
646 "cell_type": "markdown",
647 "metadata": {},
648 "source": [
649 "### Another player\n",
650 "Let's reimplement our player that does letters in frequency order. Again, this is essentially code reused from earlier."
651 ]
652 },
653 {
654 "cell_type": "code",
655 "execution_count": 76,
656 "metadata": {
657 "collapsed": false
658 },
659 "outputs": [],
660 "source": [
661 "LETTER_COUNTS = collections.Counter(l.lower() for l in open('../sherlock-holmes.txt').read() if l in string.ascii_letters)\n",
662 "LETTERS_IN_ORDER = [p[0] for p in LETTER_COUNTS.most_common()]\n",
663 "\n",
664 "class PlayerFreqOrdered:\n",
665 " def guess(self, discovered, missed, lives):\n",
666 " return [l for l in LETTERS_IN_ORDER if l not in discovered + missed][0]"
667 ]
668 },
669 {
670 "cell_type": "code",
671 "execution_count": 77,
672 "metadata": {
673 "collapsed": false
674 },
675 "outputs": [
676 {
677 "name": "stdout",
678 "output_type": "stream",
679 "text": [
680 "Target: cartons ; Discovered: ['c', 'a', 'r', 't', 'o', 'n', 's'] ; Lives remaining: 2\n"
681 ]
682 }
683 ],
684 "source": [
685 "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
686 "g.play_game()\n",
687 "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
688 ]
689 },
690 {
691 "cell_type": "code",
692 "execution_count": 78,
693 "metadata": {
694 "collapsed": false
695 },
696 "outputs": [
697 {
698 "name": "stdout",
699 "output_type": "stream",
700 "text": [
701 "Target: douching ; Discovered: ['d', 'o', 'u', 'c', 'h', 'i', 'n', '_'] ; Lives remaining: 0\n"
702 ]
703 }
704 ],
705 "source": [
706 "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
707 "g.play_game()\n",
708 "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
709 ]
710 },
711 {
712 "cell_type": "code",
713 "execution_count": 79,
714 "metadata": {
715 "collapsed": false
716 },
717 "outputs": [
718 {
719 "name": "stdout",
720 "output_type": "stream",
721 "text": [
722 "Target: minimisation ; Discovered: ['m', 'i', 'n', 'i', 'm', 'i', 's', 'a', 't', 'i', 'o', 'n'] ; Lives remaining: 4\n"
723 ]
724 }
725 ],
726 "source": [
727 "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
728 "g.play_game()\n",
729 "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
730 ]
731 },
732 {
733 "cell_type": "markdown",
734 "metadata": {},
735 "source": [
736 "## Player subclasses and inheritance\n",
737 "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",
738 "\n",
739 "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",
740 "\n",
741 "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."
742 ]
743 },
744 {
745 "cell_type": "code",
746 "execution_count": 85,
747 "metadata": {
748 "collapsed": false
749 },
750 "outputs": [],
751 "source": [
752 "class PlayerFixedOrder:\n",
753 " def __init__(self, ordered_letters):\n",
754 " self.ordered_letters = ordered_letters\n",
755 " \n",
756 " def guess(self, discovered, missed, lives):\n",
757 " return [l for l in self.ordered_letters if l not in discovered + missed][0]"
758 ]
759 },
760 {
761 "cell_type": "markdown",
762 "metadata": {},
763 "source": [
764 "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__()`."
765 ]
766 },
767 {
768 "cell_type": "code",
769 "execution_count": 86,
770 "metadata": {
771 "collapsed": false
772 },
773 "outputs": [],
774 "source": [
775 "class PlayerAlphabetical(PlayerFixedOrder):\n",
776 " def __init__(self):\n",
777 " super().__init__(string.ascii_lowercase)\n",
778 "\n",
779 "class PlayerFreqOrdered(PlayerFixedOrder):\n",
780 " def __init__(self):\n",
781 " super().__init__(LETTERS_IN_ORDER)\n"
782 ]
783 },
784 {
785 "cell_type": "markdown",
786 "metadata": {},
787 "source": [
788 "Does it work?"
789 ]
790 },
791 {
792 "cell_type": "code",
793 "execution_count": 87,
794 "metadata": {
795 "collapsed": false
796 },
797 "outputs": [
798 {
799 "name": "stdout",
800 "output_type": "stream",
801 "text": [
802 "Target: yogi ; Discovered: ['_', 'o', '_', 'i'] ; Lives remaining: 0\n"
803 ]
804 }
805 ],
806 "source": [
807 "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
808 "g.play_game()\n",
809 "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
810 ]
811 },
812 {
813 "cell_type": "code",
814 "execution_count": 88,
815 "metadata": {
816 "collapsed": false
817 },
818 "outputs": [
819 {
820 "name": "stdout",
821 "output_type": "stream",
822 "text": [
823 "Target: slimmest ; Discovered: ['s', 'l', 'i', 'm', 'm', 'e', 's', 't'] ; Lives remaining: 3\n"
824 ]
825 }
826 ],
827 "source": [
828 "g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
829 "g.play_game()\n",
830 "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
831 ]
832 },
833 {
834 "cell_type": "code",
835 "execution_count": 89,
836 "metadata": {
837 "collapsed": false
838 },
839 "outputs": [
840 {
841 "name": "stdout",
842 "output_type": "stream",
843 "text": [
844 "Target: diseases ; Discovered: ['d', 'i', '_', 'e', 'a', '_', 'e', '_'] ; Lives remaining: 0\n"
845 ]
846 }
847 ],
848 "source": [
849 "g = Game(random.choice(WORDS), player=PlayerAlphabetical())\n",
850 "g.play_game()\n",
851 "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
852 ]
853 },
854 {
855 "cell_type": "markdown",
856 "metadata": {},
857 "source": [
858 "Can we use the generic version directly?"
859 ]
860 },
861 {
862 "cell_type": "code",
863 "execution_count": 90,
864 "metadata": {
865 "collapsed": false
866 },
867 "outputs": [
868 {
869 "name": "stdout",
870 "output_type": "stream",
871 "text": [
872 "Target: pardon ; Discovered: ['p', '_', 'r', '_', 'o', 'n'] ; Lives remaining: 0\n"
873 ]
874 }
875 ],
876 "source": [
877 "g = Game(random.choice(WORDS), player=PlayerFixedOrder(list(reversed(string.ascii_lowercase))))\n",
878 "g.play_game()\n",
879 "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
880 ]
881 },
882 {
883 "cell_type": "code",
884 "execution_count": 91,
885 "metadata": {
886 "collapsed": false
887 },
888 "outputs": [
889 {
890 "name": "stdout",
891 "output_type": "stream",
892 "text": [
893 "Target: grouchier ; Discovered: ['_', 'r', 'o', 'u', '_', '_', '_', '_', 'r'] ; Lives remaining: 0\n"
894 ]
895 }
896 ],
897 "source": [
898 "g = Game(random.choice(WORDS), player=PlayerFixedOrder(list(reversed(string.ascii_lowercase))))\n",
899 "g.play_game()\n",
900 "print('Target:', g.target, '; Discovered:', g.discovered, '; Lives remaining:', g.lives)"
901 ]
902 },
903 {
904 "cell_type": "markdown",
905 "metadata": {},
906 "source": [
907 "## Mass gaming\n",
908 "Now we can get machines playing each other, we get get them playing *lots* of games and just tell us the summaries. \n",
909 "\n",
910 "Which of these three methods (alphabetical, frequency ordered, reverse alphabetical) is the best? Let's get each player to play 1000 random games and see which gets the most wins."
911 ]
912 },
913 {
914 "cell_type": "code",
915 "execution_count": 92,
916 "metadata": {
917 "collapsed": false
918 },
919 "outputs": [
920 {
921 "name": "stdout",
922 "output_type": "stream",
923 "text": [
924 "49\n"
925 ]
926 }
927 ],
928 "source": [
929 "wins = 0\n",
930 "for _ in range(1000):\n",
931 " g = Game(random.choice(WORDS), player=PlayerAlphabetical())\n",
932 " g.play_game()\n",
933 " if g.game_won:\n",
934 " wins += 1\n",
935 "print(wins)"
936 ]
937 },
938 {
939 "cell_type": "code",
940 "execution_count": 96,
941 "metadata": {
942 "collapsed": false
943 },
944 "outputs": [
945 {
946 "name": "stdout",
947 "output_type": "stream",
948 "text": [
949 "309\n"
950 ]
951 }
952 ],
953 "source": [
954 "wins = 0\n",
955 "for _ in range(1000):\n",
956 " g = Game(random.choice(WORDS), player=PlayerFreqOrdered())\n",
957 " g.play_game()\n",
958 " if g.game_won:\n",
959 " wins += 1\n",
960 "print(wins)"
961 ]
962 },
963 {
964 "cell_type": "code",
965 "execution_count": 94,
966 "metadata": {
967 "collapsed": false
968 },
969 "outputs": [
970 {
971 "name": "stdout",
972 "output_type": "stream",
973 "text": [
974 "6\n"
975 ]
976 }
977 ],
978 "source": [
979 "wins = 0\n",
980 "for _ in range(1000):\n",
981 " g = Game(random.choice(WORDS), player=PlayerFixedOrder(list(reversed(string.ascii_lowercase))))\n",
982 " g.play_game()\n",
983 " if g.game_won:\n",
984 " wins += 1\n",
985 "print(wins)"
986 ]
987 },
988 {
989 "cell_type": "markdown",
990 "metadata": {},
991 "source": [
992 "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",
993 "\n",
994 "That's the next part..."
995 ]
996 },
997 {
998 "cell_type": "code",
999 "execution_count": null,
1000 "metadata": {
1001 "collapsed": false
1002 },
1003 "outputs": [],
1004 "source": []
1005 }
1006 ],
1007 "metadata": {
1008 "kernelspec": {
1009 "display_name": "Python 3",
1010 "language": "python",
1011 "name": "python3"
1012 },
1013 "language_info": {
1014 "codemirror_mode": {
1015 "name": "ipython",
1016 "version": 3
1017 },
1018 "file_extension": ".py",
1019 "mimetype": "text/x-python",
1020 "name": "python",
1021 "nbconvert_exporter": "python",
1022 "pygments_lexer": "ipython3",
1023 "version": "3.5.2+"
1024 }
1025 },
1026 "nbformat": 4,
1027 "nbformat_minor": 0
1028 }