5 "execution_count": 106,
13 "import collections\n",
17 "from enum import Enum\n",
18 "Direction = Enum('Direction', 'left right up down upleft upright downleft downright')\n",
20 "delta = {Direction.left: (0, -1),Direction.right: (0, 1), \n",
21 " Direction.up: (-1, 0), Direction.down: (1, 0), \n",
22 " Direction.upleft: (-1, -1), Direction.upright: (-1, 1), \n",
23 " Direction.downleft: (1, -1), Direction.downright: (1, 1)}\n",
32 "execution_count": 46,
38 "def empty_grid(w, h):\n",
39 " return [['.' for c in range(w)] for r in range(h)]"
44 "execution_count": 47,
50 "def show_grid(grid):\n",
51 " return lcat(cat(r) for r in grid)"
56 "execution_count": 48,
62 "def indices(grid, r, c, l, d):\n",
63 " dr, dc = delta[d]\n",
64 " w = len(grid[0])\n",
66 " inds = [(r + i * dr, c + i * dc) for i in range(l)]\n",
67 " return [(i, j) for i, j in inds\n",
76 "execution_count": 49,
82 "def gslice(grid, r, c, l, d):\n",
83 " return [grid[i][j] for i, j in indices(grid, r, c, l, d)]"
88 "execution_count": 50,
94 "def set_grid(grid, r, c, d, word):\n",
95 " for (i, j), l in zip(indices(grid, r, c, len(word), d), word):\n",
102 "execution_count": 51,
108 "def could_add(grid, r, c, d, word):\n",
109 " s = gslice(grid, r, c, len(word), d)\n",
110 " return re.fullmatch(cat(s), word)"
115 "execution_count": 52,
121 "def present(grid, word):\n",
122 " w = len(grid[0])\n",
124 " for r in range(h):\n",
125 " for c in range(w):\n",
126 " for d in Direction:\n",
127 " if cat(gslice(grid, r, c, len(word), d)) == word:\n",
128 " return True, r, c, d\n",
129 " return False, 0, 0, list(Direction)[0]"
134 "execution_count": 53,
140 "def pad_grid(g0):\n",
141 " grid = copy.deepcopy(g0)\n",
142 " w = len(grid[0])\n",
144 " for r in range(h):\n",
145 " for c in range(w):\n",
146 " if grid[r][c] == '.':\n",
147 " grid[r][c] = random_wordsearch_letter()\n",
153 "execution_count": 54,
159 "def read_wordsearch(fn):\n",
160 " lines = [l.strip() for l in open(fn).readlines()]\n",
161 " w, h = [int(s) for s in lines[0].split('x')]\n",
162 " grid = lines[1:h+1]\n",
163 " words = lines[h+1:]\n",
164 " return w, h, grid, words"
169 "execution_count": 77,
178 "(['iisnoitpircserpoacos',\n",
179 " 'eohgiodnpgbeautypoar',\n",
180 " 'arsllorcsnestdomofne',\n",
181 " 'irfdeemseirrgarnlfrb',\n",
182 " 'tclumpingkoeasevoedm',\n",
183 " 'hetobsibecgftdcmgrvi',\n",
184 " 'isesrepoeuelmsriyset',\n",
185 " 'eknaodouusnoedetoxes',\n",
186 " 'vldwiarsoiogsstiedue',\n",
187 " 'ehealcegdsuidsesuifi',\n",
188 " 'dirrsiidnesepmdaelnc',\n",
189 " 'nierrrsdeibuwpegriga',\n",
190 " 'sprirrsyebniednusirb',\n",
191 " 'eeboraisahmieeaycnnw',\n",
192 " 'irrrnbrnepcmanhriuug',\n",
193 " 'tjasdypeykuanppetiot',\n",
194 " 'nuvaaaicssndonrppmer',\n",
195 " 'areeioassenyocoaooze',\n",
196 " 'hellesoamalabruptest',\n",
197 " 'csygsfyosinightclubs'],\n",
211 " 'decriminalisation',\n",
218 " 'encirclement',\n",
226 " 'interpolations',\n",
229 " 'lithographed',\n",
239 " 'prescriptions',\n",
260 "execution_count": 77,
262 "output_type": "execute_result"
266 "width, height, g, ws = read_wordsearch('wordsearch1.txt')\n",
272 "execution_count": 78,
280 "output_type": "stream",
282 "abruptest (True, 18, 11, <Direction.right: 2>)\n",
283 "apology (True, 0, 16, <Direction.down: 4>)\n",
284 "assumed (True, 18, 7, <Direction.upright: 6>)\n",
285 "barricades (True, 14, 5, <Direction.up: 3>)\n",
286 "beauty (True, 1, 10, <Direction.right: 2>)\n",
287 "bravely (True, 13, 2, <Direction.down: 4>)\n",
288 "bravos (False, 0, 0, <Direction.left: 1>)\n",
289 "burlier (False, 0, 0, <Direction.left: 1>)\n",
290 "chanties (True, 19, 0, <Direction.up: 3>)\n",
291 "clumping (True, 4, 1, <Direction.right: 2>)\n",
292 "coached (True, 17, 13, <Direction.upleft: 5>)\n",
293 "coffers (True, 0, 17, <Direction.down: 4>)\n",
294 "coyness (True, 17, 13, <Direction.left: 1>)\n",
295 "decriminalisation (False, 0, 0, <Direction.left: 1>)\n",
296 "detoxes (True, 7, 13, <Direction.right: 2>)\n",
297 "differs (False, 0, 0, <Direction.left: 1>)\n",
298 "duelled (False, 0, 0, <Direction.left: 1>)\n",
299 "duplicating (False, 0, 0, <Direction.left: 1>)\n",
300 "elaborates (False, 0, 0, <Direction.left: 1>)\n",
301 "embroils (True, 3, 4, <Direction.down: 4>)\n",
302 "encirclement (False, 0, 0, <Direction.left: 1>)\n",
303 "erogenous (True, 2, 10, <Direction.down: 4>)\n",
304 "facsimiled (False, 0, 0, <Direction.left: 1>)\n",
305 "festers (False, 0, 0, <Direction.left: 1>)\n",
306 "flickering (False, 0, 0, <Direction.left: 1>)\n",
307 "fusible (False, 0, 0, <Direction.left: 1>)\n",
308 "gluiest (True, 11, 18, <Direction.upleft: 5>)\n",
309 "golfers (True, 8, 11, <Direction.up: 3>)\n",
310 "interpolations (False, 0, 0, <Direction.left: 1>)\n",
311 "involved (False, 0, 0, <Direction.left: 1>)\n",
312 "irony (True, 11, 1, <Direction.downright: 8>)\n",
313 "lithographed (False, 0, 0, <Direction.left: 1>)\n",
314 "nabbed (True, 14, 7, <Direction.upright: 6>)\n",
315 "nightclubs (True, 19, 10, <Direction.right: 2>)\n",
316 "oblongs (False, 0, 0, <Direction.left: 1>)\n",
317 "optics (True, 17, 16, <Direction.up: 3>)\n",
318 "orphaned (True, 17, 14, <Direction.up: 3>)\n",
319 "overstates (False, 0, 0, <Direction.left: 1>)\n",
320 "paining (True, 15, 13, <Direction.upleft: 5>)\n",
321 "papery (True, 18, 15, <Direction.up: 3>)\n",
322 "perjures (True, 12, 1, <Direction.down: 4>)\n",
323 "prescriptions (True, 0, 14, <Direction.left: 1>)\n",
324 "prissier (True, 15, 6, <Direction.up: 3>)\n",
325 "rallies (False, 0, 0, <Direction.left: 1>)\n",
326 "rebated (False, 0, 0, <Direction.left: 1>)\n",
327 "reneges (False, 0, 0, <Direction.left: 1>)\n",
328 "saleswomen (False, 0, 0, <Direction.left: 1>)\n",
329 "scrolls (True, 2, 8, <Direction.left: 1>)\n",
330 "searing (True, 8, 13, <Direction.downright: 8>)\n",
331 "slobbering (False, 0, 0, <Direction.left: 1>)\n",
332 "soups (True, 9, 9, <Direction.upleft: 5>)\n",
333 "sucking (True, 7, 9, <Direction.up: 3>)\n",
334 "tenderer (True, 5, 2, <Direction.down: 4>)\n",
335 "thieved (True, 4, 0, <Direction.down: 4>)\n",
336 "timbers (True, 6, 19, <Direction.up: 3>)\n",
337 "toiletries (False, 0, 0, <Direction.left: 1>)\n",
338 "unabashed (False, 0, 0, <Direction.left: 1>)\n",
339 "warriors (True, 8, 3, <Direction.down: 4>)\n",
340 "wimpy (True, 11, 12, <Direction.downleft: 7>)\n",
341 "wriest (True, 13, 19, <Direction.upleft: 5>)\n"
347 " print(w, present(g, w))"
351 "cell_type": "markdown",
354 "Which words are present?"
359 "execution_count": 79,
391 " 'prescriptions',\n",
405 "execution_count": 79,
407 "output_type": "execute_result"
411 "[w for w in ws if present(g, w)[0]]"
415 "cell_type": "markdown",
418 "What is the longest word that is present?"
423 "execution_count": 88,
434 "execution_count": 88,
436 "output_type": "execute_result"
440 "sorted([w for w in ws if present(g, w)[0]], key=len)[-1]"
444 "cell_type": "markdown",
447 "What is the longest word that is absent?"
452 "execution_count": 89,
460 "'decriminalisation'"
463 "execution_count": 89,
465 "output_type": "execute_result"
469 "sorted([w for w in ws if not present(g, w)[0]], key=len)[-1]"
473 "cell_type": "markdown",
476 "How many letters are unused?"
481 "execution_count": 82,
492 "execution_count": 82,
494 "output_type": "execute_result"
498 "g0 = empty_grid(width, height)\n",
500 " p, r, c, d = present(g, w)\n",
502 " set_grid(g0, r, c, d, w)\n",
503 "len([c for c in cat(cat(l) for l in g0) if c == '.'])"
507 "cell_type": "markdown",
510 "What is the longest word you can make form the leftover letters?"
515 "execution_count": 83,
524 "Counter({'a': 11,\n",
548 "execution_count": 83,
550 "output_type": "execute_result"
554 "unused_letters = [l for l, u in zip((c for c in cat(cat(l) for l in g)), (c for c in cat(cat(l) for l in g0)))\n",
556 "unused_letter_count = collections.Counter(unused_letters)\n",
557 "unused_letter_count"
562 "execution_count": 84,
572 " 'decriminalisation',\n",
577 " 'encirclement',\n",
582 " 'interpolations',\n",
584 " 'lithographed',\n",
596 "execution_count": 84,
598 "output_type": "execute_result"
602 "unused_words = [w for w in ws if not present(g, w)[0]]\n",
608 "execution_count": 85,
615 "output_type": "stream",
617 "*bravos Counter({'v': 1, 'b': 1, 'a': 1, 's': 1, 'o': 1, 'r': 1})\n",
618 "*burlier Counter({'r': 2, 'b': 1, 'u': 1, 'l': 1, 'i': 1, 'e': 1})\n",
619 "*decriminalisation Counter({'i': 4, 'n': 2, 'a': 2, 's': 1, 'l': 1, 'd': 1, 'e': 1, 'r': 1, 't': 1, 'm': 1, 'o': 1, 'c': 1})\n",
620 "*differs Counter({'f': 2, 's': 1, 'i': 1, 'd': 1, 'e': 1, 'r': 1})\n",
621 "*duelled Counter({'d': 2, 'e': 2, 'l': 2, 'u': 1})\n",
622 "*duplicating Counter({'i': 2, 'g': 1, 'n': 1, 'a': 1, 'l': 1, 'd': 1, 't': 1, 'p': 1, 'u': 1, 'c': 1})\n",
623 "*elaborates Counter({'a': 2, 'e': 2, 'b': 1, 's': 1, 'l': 1, 'o': 1, 'r': 1, 't': 1})\n",
624 "*encirclement Counter({'e': 3, 'n': 2, 'c': 2, 'm': 1, 't': 1, 'l': 1, 'i': 1, 'r': 1})\n",
625 "*facsimiled Counter({'i': 2, 's': 1, 'm': 1, 'a': 1, 'f': 1, 'l': 1, 'd': 1, 'e': 1, 'c': 1})\n",
626 "*festers Counter({'s': 2, 'e': 2, 'f': 1, 'r': 1, 't': 1})\n",
627 "*flickering Counter({'i': 2, 'g': 1, 'n': 1, 'f': 1, 'l': 1, 'e': 1, 'k': 1, 'r': 1, 'c': 1})\n",
628 "*fusible Counter({'s': 1, 'e': 1, 'f': 1, 'b': 1, 'i': 1, 'l': 1, 'u': 1})\n",
629 "*interpolations Counter({'n': 2, 'i': 2, 'o': 2, 't': 2, 's': 1, 'a': 1, 'l': 1, 'e': 1, 'p': 1, 'r': 1})\n",
630 "*involved Counter({'v': 2, 'n': 1, 'l': 1, 'i': 1, 'o': 1, 'e': 1, 'd': 1})\n",
631 "*lithographed Counter({'h': 2, 'g': 1, 'a': 1, 'r': 1, 'l': 1, 'i': 1, 'o': 1, 'e': 1, 'p': 1, 'd': 1, 't': 1})\n",
632 "*oblongs Counter({'o': 2, 'b': 1, 'g': 1, 'n': 1, 'l': 1, 's': 1})\n",
633 "*overstates Counter({'s': 2, 'e': 2, 't': 2, 'v': 1, 'a': 1, 'o': 1, 'r': 1})\n",
634 "*rallies Counter({'l': 2, 's': 1, 'a': 1, 'i': 1, 'e': 1, 'r': 1})\n",
635 "*rebated Counter({'e': 2, 'b': 1, 'a': 1, 'd': 1, 'r': 1, 't': 1})\n",
636 "*reneges Counter({'e': 3, 's': 1, 'g': 1, 'n': 1, 'r': 1})\n",
637 "saleswomen Counter({'s': 2, 'e': 2, 'm': 1, 'n': 1, 'a': 1, 'l': 1, 'o': 1, 'w': 1})\n",
638 "*slobbering Counter({'b': 2, 's': 1, 'g': 1, 'n': 1, 'i': 1, 'o': 1, 'e': 1, 'l': 1, 'r': 1})\n",
639 "*toiletries Counter({'i': 2, 'e': 2, 't': 2, 's': 1, 'l': 1, 'o': 1, 'r': 1})\n",
640 "*unabashed Counter({'a': 2, 'b': 1, 'n': 1, 'h': 1, 's': 1, 'd': 1, 'e': 1, 'u': 1})\n"
645 "makeable_words = []\n",
646 "for w in unused_words:\n",
647 " unused_word_count = collections.Counter(w)\n",
648 " if all(l in unused_letter_count and unused_word_count[l] <= unused_letter_count[l] for l in unused_word_count):\n",
649 " makeable_words += [w]\n",
650 " print('*', end='')\n",
651 " print(w, unused_word_count)"
656 "execution_count": 86,
667 "execution_count": 86,
669 "output_type": "execute_result"
673 "max(len(w) for w in makeable_words)"
678 "execution_count": 90,
686 "'decriminalisation'"
689 "execution_count": 90,
691 "output_type": "execute_result"
695 "sorted(makeable_words, key=len)[-1]"
700 "execution_count": 103,
706 "def do_wordsearch_tasks(fn):\n",
707 " width, height, grid, words = read_wordsearch(fn)\n",
708 " unused_words = [w for w in words if not present(grid, w)[0]]\n",
709 " lwp = sorted([w for w in words if present(grid, w)[0]], key=len)[-1]\n",
710 " lwa = sorted(unused_words, key=len)[-1]\n",
711 " g0 = empty_grid(width, height)\n",
712 " for w in words:\n",
713 " p, r, c, d = present(grid, w)\n",
715 " set_grid(g0, r, c, d, w) \n",
716 " unused_letters = [l for l, u in zip((c for c in cat(cat(l) for l in grid)), (c for c in cat(cat(l) for l in g0)))\n",
718 " unused_letter_count = collections.Counter(unused_letters)\n",
719 " makeable_words = []\n",
720 " for w in unused_words:\n",
721 " unused_word_count = collections.Counter(w)\n",
722 " if all(l in unused_letter_count and unused_word_count[l] <= unused_letter_count[l] for l in unused_word_count):\n",
723 " makeable_words += [w]\n",
724 " lwm = sorted(makeable_words, key=len)[-1]\n",
725 " print('\\n{}'.format(fn))\n",
726 " print('Longest word present: {}, {} letters'.format(lwp, len(lwp)))\n",
727 " print('Longest word absent: {}, {} letters'.format(lwa, len(lwa)))\n",
728 " print('{} unused letters'.format(len([c for c in cat(cat(l) for l in g0) if c == '.'])))\n",
729 " print('Longest makeable word: {}, {}'.format(lwm, len(lwm)))"
734 "execution_count": 112,
741 "output_type": "stream",
744 "wordsearch00.txt\n",
745 "Longest word present: yellowing, 9 letters\n",
746 "Longest word absent: sequined, 8 letters\n",
747 "147 unused letters\n",
748 "Longest makeable word: pettiest, 8\n",
750 "wordsearch01.txt\n",
751 "Longest word present: soubriquets, 11 letters\n",
752 "Longest word absent: swallowing, 10 letters\n",
753 "124 unused letters\n",
754 "Longest makeable word: extradited, 10\n",
756 "wordsearch02.txt\n",
757 "Longest word present: unattended, 10 letters\n",
758 "Longest word absent: runabouts, 9 letters\n",
759 "120 unused letters\n",
760 "Longest makeable word: runabouts, 9\n",
762 "wordsearch03.txt\n",
763 "Longest word present: indemnifications, 16 letters\n",
764 "Longest word absent: propagandised, 13 letters\n",
765 "129 unused letters\n",
766 "Longest makeable word: propagandised, 13\n",
768 "wordsearch04.txt\n",
769 "Longest word present: ostentatiously, 14 letters\n",
770 "Longest word absent: oleomargarine, 13 letters\n",
771 "128 unused letters\n",
772 "Longest makeable word: oleomargarine, 13\n",
774 "wordsearch05.txt\n",
775 "Longest word present: straightjacketing, 17 letters\n",
776 "Longest word absent: grandiloquence, 14 letters\n",
777 "115 unused letters\n",
778 "Longest makeable word: multivitamins, 13\n",
780 "wordsearch06.txt\n",
781 "Longest word present: inflorescence, 13 letters\n",
782 "Longest word absent: extinguished, 12 letters\n",
783 "159 unused letters\n",
784 "Longest makeable word: convocations, 12\n",
786 "wordsearch07.txt\n",
787 "Longest word present: hypothesising, 13 letters\n",
788 "Longest word absent: nonrenewable, 12 letters\n",
789 "127 unused letters\n",
790 "Longest makeable word: nonrenewable, 12\n",
792 "wordsearch08.txt\n",
793 "Longest word present: misrepresents, 13 letters\n",
794 "Longest word absent: predominates, 12 letters\n",
795 "125 unused letters\n",
796 "Longest makeable word: predominates, 12\n",
798 "wordsearch09.txt\n",
799 "Longest word present: counterattacks, 14 letters\n",
800 "Longest word absent: overstepping, 12 letters\n",
801 "125 unused letters\n",
802 "Longest makeable word: constituents, 12\n",
804 "wordsearch10.txt\n",
805 "Longest word present: cheerlessness, 13 letters\n",
806 "Longest word absent: gregariously, 12 letters\n",
807 "142 unused letters\n",
808 "Longest makeable word: gregariously, 12\n",
810 "wordsearch11.txt\n",
811 "Longest word present: petitioners, 11 letters\n",
812 "Longest word absent: overdosing, 10 letters\n",
813 "137 unused letters\n",
814 "Longest makeable word: needlessly, 10\n",
816 "wordsearch12.txt\n",
817 "Longest word present: propagandises, 13 letters\n",
818 "Longest word absent: fluorescent, 11 letters\n",
819 "130 unused letters\n",
820 "Longest makeable word: fluorescent, 11\n",
822 "wordsearch13.txt\n",
823 "Longest word present: preregisters, 12 letters\n",
824 "Longest word absent: undergrowth, 11 letters\n",
825 "113 unused letters\n",
826 "Longest makeable word: undergrowth, 11\n",
828 "wordsearch14.txt\n",
829 "Longest word present: dispossessing, 13 letters\n",
830 "Longest word absent: sweatshirts, 11 letters\n",
831 "116 unused letters\n",
832 "Longest makeable word: sweatshirts, 11\n",
834 "wordsearch15.txt\n",
835 "Longest word present: retrenching, 11 letters\n",
836 "Longest word absent: quadruples, 10 letters\n",
837 "119 unused letters\n",
838 "Longest makeable word: cavillings, 10\n",
840 "wordsearch16.txt\n",
841 "Longest word present: cantankerously, 14 letters\n",
842 "Longest word absent: amorphousness, 13 letters\n",
843 "101 unused letters\n",
844 "Longest makeable word: amorphousness, 13\n",
846 "wordsearch17.txt\n",
847 "Longest word present: appreciating, 12 letters\n",
848 "Longest word absent: unreasoning, 11 letters\n",
849 "114 unused letters\n",
850 "Longest makeable word: unreasoning, 11\n",
852 "wordsearch18.txt\n",
853 "Longest word present: rehabilitates, 13 letters\n",
854 "Longest word absent: interlarding, 12 letters\n",
855 "135 unused letters\n",
856 "Longest makeable word: interlarding, 12\n",
858 "wordsearch19.txt\n",
859 "Longest word present: predetermines, 13 letters\n",
860 "Longest word absent: prosperously, 12 letters\n",
861 "127 unused letters\n",
862 "Longest makeable word: kneecapping, 11\n"
867 "for fn in sorted(os.listdir()):\n",
868 " if re.match('wordsearch\\d\\d\\.txt', fn):\n",
869 " do_wordsearch_tasks(fn)"
874 "execution_count": null,
884 "display_name": "Python 3",
885 "language": "python",
893 "file_extension": ".py",
894 "mimetype": "text/x-python",
896 "nbconvert_exporter": "python",
897 "pygments_lexer": "ipython3",