4 "cell_type": "markdown",
8 "Given a text file, consisting of three parts (a grid size, a grid, and a list of words), find:\n",
9 "* the words present in the grid, \n",
10 "* the longest word present in the grid, \n",
11 "* the number of words not present in the grid, \n",
12 "* the longest word not present that can be formed from the leftover letters\n",
14 "The only words that need be considered are the ones given in the list in the puzzle input.\n",
16 "The puzzle consists of:\n",
17 "1. A line consisting of _w_`x`_h_, where _w_ and _h_ are integers giving the width and height of the grid.\n",
18 "2. The grid itself, consisting of _h_ lines each of _w_ letters.\n",
19 "3. A list of words, one word per line, of arbitrary length. "
23 "cell_type": "markdown",
53 "14 words added; 6 directions\n",
55 "Present: apace cowhides crazies dock knows lived mown pears probed rhubarb rioted staple tops wide\n",
57 "Decoys: adapting bombing boor brick cackles carnal casino chaplets chump coaster coccyxes coddle collies creels crumbled cunt curds curled curlier deepen demeanor dicier dowses ensuing faddish fest fickler foaming gambol garoting gliding gristle grunts guts ibex impugns instants kielbasy lanyard loamier lugs market meanly minuend misprint mitts molested moonshot mucking oaks olives orgasmic pastrami perfect proceed puckered quashed refined regards retraces revel ridges ringlet scoff shinier siren solaria sprain sunder sunup tamped tapes thirds throw tiller times trains tranquil transfix typesets uric wariness welts whimsy winced winced\n",
59 "Decoys: fickler, adapting, chump, foaming, molested, carnal, crumbled, guts, minuend, bombing, winced, coccyxes, solaria, shinier, cackles\n",
61 "All words: adapting, apace, bombing, cackles, carnal, chump, coccyxes, cowhides, crazies, crumbled, dock, fickler, foaming, guts, knows, lived, minuend, molested, mown, pears, probed, rhubarb, rioted, shinier, solaria, staple, tops, wide, winced\n",
63 "Directions: [('probed', '`(True, 3, 0, <Direction.down: 4>)`'), ('staple', '`(True, 9, 1, <Direction.right: 2>)`'), ('rioted', '`(True, 6, 7, <Direction.upleft: 5>)`'), ('cowhides', '`(True, 8, 3, <Direction.up: 3>)`'), ('tops', '`(True, 7, 4, <Direction.right: 2>)`'), ('knows', '`(True, 8, 2, <Direction.up: 3>)`'), ('lived', '`(True, 2, 4, <Direction.downright: 8>)`'), ('rhubarb', '`(True, 2, 9, <Direction.down: 4>)`'), ('crazies', '`(True, 7, 1, <Direction.up: 3>)`'), ('dock', '`(True, 8, 5, <Direction.up: 3>)`'), ('apace', '`(True, 5, 8, <Direction.up: 3>)`'), ('mown', '`(True, 0, 5, <Direction.right: 2>)`'), ('pears', '`(True, 0, 3, <Direction.downright: 8>)`'), ('wide', '`(True, 4, 4, <Direction.upright: 6>)`')]"
76 "import collections\n",
80 "from enum import Enum\n",
81 "Direction = Enum('Direction', 'left right up down upleft upright downleft downright')\n",
83 "delta = {Direction.left: (0, -1),Direction.right: (0, 1), \n",
84 " Direction.up: (-1, 0), Direction.down: (1, 0), \n",
85 " Direction.upleft: (-1, -1), Direction.upright: (-1, 1), \n",
86 " Direction.downleft: (1, -1), Direction.downright: (1, 1)}\n",
101 "def empty_grid(w, h):\n",
102 " return [['.' for c in range(w)] for r in range(h)]"
107 "execution_count": 6,
113 "def show_grid(grid):\n",
114 " return lcat(cat(r) for r in grid)"
119 "execution_count": 7,
125 "def indices(grid, r, c, l, d):\n",
126 " dr, dc = delta[d]\n",
127 " w = len(grid[0])\n",
129 " inds = [(r + i * dr, c + i * dc) for i in range(l)]\n",
130 " return [(i, j) for i, j in inds\n",
139 "execution_count": 8,
145 "def gslice(grid, r, c, l, d):\n",
146 " return [grid[i][j] for i, j in indices(grid, r, c, l, d)]"
151 "execution_count": 9,
157 "def set_grid(grid, r, c, d, word):\n",
158 " for (i, j), l in zip(indices(grid, r, c, len(word), d), word):\n",
165 "execution_count": 10,
171 "def present(grid, word):\n",
172 " w = len(grid[0])\n",
174 " for r in range(h):\n",
175 " for c in range(w):\n",
176 " for d in Direction:\n",
177 " if cat(gslice(grid, r, c, len(word), d)) == word:\n",
178 " return True, r, c, d\n",
179 " return False, 0, 0, list(Direction)[0]"
184 "execution_count": 158,
190 "def present_many(grid, words):\n",
191 " w = len(grid[0])\n",
193 " wordlens = set(len(w) for w in words)\n",
195 " for r in range(h):\n",
196 " for c in range(w):\n",
197 " for d in Direction:\n",
198 " for wordlen in wordlens:\n",
199 " word = cat(gslice(grid, r, c, wordlen, d))\n",
200 " if word in words:\n",
201 " presences += [(word, r, c, d)]\n",
202 " return set(presences)"
207 "execution_count": 141,
213 "# def present_many(grid, words):\n",
214 "# w = len(grid[0])\n",
216 "# wordlens = set(len(w) for w in words)\n",
217 "# presences = set()\n",
218 "# for r in range(h):\n",
219 "# for c in range(w):\n",
220 "# for d in Direction:\n",
221 "# for wordlen in wordlens:\n",
222 "# word = cat(gslice(grid, r, c, wordlen, d))\n",
223 "# if word in words:\n",
224 "# presences.add((word, r, c, d))\n",
230 "execution_count": 11,
236 "def read_wordsearch(fn):\n",
237 " lines = [l.strip() for l in open(fn).readlines()]\n",
238 " w, h = [int(s) for s in lines[0].split('x')]\n",
239 " grid = lines[1:h+1]\n",
240 " words = lines[h+1:]\n",
241 " return w, h, grid, words"
246 "execution_count": 49,
254 "(['pistrohxegniydutslxt',\n",
255 " 'wmregunarbpiledsyuoo',\n",
256 " 'hojminbmutartslrlmgo',\n",
257 " 'isrsdniiekildabolpll',\n",
258 " 'tstsnyekentypkalases',\n",
259 " 'ssnetengcrfetedirgdt',\n",
260 " 'religstasuslatxauner',\n",
261 " 'elgcpgatsklglzistilo',\n",
262 " 'tndlimitationilkasan',\n",
263 " 'aousropedlygiifeniog',\n",
264 " 'kilrprepszffsyzqsrhs',\n",
265 " 'itlaadorableorpccese',\n",
266 " 'noaeewoodedpngmqicnl',\n",
267 " 'gmrtoitailingchelrok',\n",
268 " 'jadsngninetsahtooeic',\n",
269 " 'xeernighestsailarmtu',\n",
270 " 'aeabsolvednscumdfnon',\n",
271 " 'gydammingawlcandornk',\n",
272 " 'hurlerslvkaccxcinosw',\n",
273 " 'iqnanoitacifitrofqqi'],\n",
300 " 'fortification',\n",
309 " 'hopelessness',\n",
317 " 'justification',\n",
344 " 'reformulated',\n",
376 "execution_count": 49,
378 "output_type": "execute_result"
382 "width, height, g, ws = read_wordsearch('wordsearch04.txt')\n",
388 "execution_count": 50,
395 "output_type": "stream",
397 "absolved (True, 16, 2, <Direction.right: 2>)\n",
398 "adorable (True, 11, 4, <Direction.right: 2>)\n",
399 "aeon (True, 11, 4, <Direction.down: 4>)\n",
400 "alias (True, 15, 15, <Direction.left: 1>)\n",
401 "ancestor (False, 0, 0, <Direction.left: 1>)\n",
402 "baritone (False, 0, 0, <Direction.left: 1>)\n",
403 "bemusing (False, 0, 0, <Direction.left: 1>)\n",
404 "blonds (False, 0, 0, <Direction.left: 1>)\n",
405 "bran (True, 1, 9, <Direction.left: 1>)\n",
406 "calcite (True, 19, 9, <Direction.upright: 6>)\n",
407 "candor (True, 17, 12, <Direction.right: 2>)\n",
408 "conciseness (False, 0, 0, <Direction.left: 1>)\n",
409 "consequent (False, 0, 0, <Direction.left: 1>)\n",
410 "cuddle (False, 0, 0, <Direction.left: 1>)\n",
411 "damming (True, 17, 2, <Direction.right: 2>)\n",
412 "dashboards (False, 0, 0, <Direction.left: 1>)\n",
413 "despairing (False, 0, 0, <Direction.left: 1>)\n",
414 "dint (False, 0, 0, <Direction.left: 1>)\n",
415 "dullard (True, 8, 2, <Direction.down: 4>)\n",
416 "dynasty (True, 3, 4, <Direction.downright: 8>)\n",
417 "employer (False, 0, 0, <Direction.left: 1>)\n",
418 "exhorts (True, 0, 8, <Direction.left: 1>)\n",
419 "feted (True, 5, 10, <Direction.right: 2>)\n",
420 "fill (True, 9, 14, <Direction.upleft: 5>)\n",
421 "flattens (True, 10, 10, <Direction.upleft: 5>)\n",
422 "foghorn (True, 10, 11, <Direction.downright: 8>)\n",
423 "fortification (True, 19, 16, <Direction.left: 1>)\n",
424 "freakish (False, 0, 0, <Direction.left: 1>)\n",
425 "frolics (True, 16, 16, <Direction.up: 3>)\n",
426 "gall (False, 0, 0, <Direction.left: 1>)\n",
427 "gees (True, 17, 0, <Direction.upright: 6>)\n",
428 "genies (True, 5, 7, <Direction.upleft: 5>)\n",
429 "gets (True, 6, 4, <Direction.upleft: 5>)\n",
430 "hastening (True, 14, 13, <Direction.left: 1>)\n",
431 "hits (True, 2, 0, <Direction.down: 4>)\n",
432 "hopelessness (False, 0, 0, <Direction.left: 1>)\n",
433 "hurlers (True, 18, 0, <Direction.right: 2>)\n",
434 "impales (False, 0, 0, <Direction.left: 1>)\n",
435 "infix (False, 0, 0, <Direction.left: 1>)\n",
436 "inflow (False, 0, 0, <Direction.left: 1>)\n",
437 "innumerable (False, 0, 0, <Direction.left: 1>)\n",
438 "intentional (False, 0, 0, <Direction.left: 1>)\n",
439 "jerkin (False, 0, 0, <Direction.left: 1>)\n",
440 "justification (False, 0, 0, <Direction.left: 1>)\n",
441 "kitty (True, 8, 15, <Direction.upleft: 5>)\n",
442 "knuckles (True, 17, 19, <Direction.up: 3>)\n",
443 "leaving (False, 0, 0, <Direction.left: 1>)\n",
444 "like (True, 3, 11, <Direction.left: 1>)\n",
445 "limitation (True, 8, 3, <Direction.right: 2>)\n",
446 "locoweeds (False, 0, 0, <Direction.left: 1>)\n",
447 "loot (True, 3, 19, <Direction.up: 3>)\n",
448 "lucking (True, 7, 10, <Direction.upleft: 5>)\n",
449 "lumps (True, 0, 17, <Direction.down: 4>)\n",
450 "mercerising (True, 15, 17, <Direction.up: 3>)\n",
451 "monickers (False, 0, 0, <Direction.left: 1>)\n",
452 "motionless (True, 13, 1, <Direction.up: 3>)\n",
453 "naturally (True, 9, 16, <Direction.up: 3>)\n",
454 "nighest (True, 15, 4, <Direction.right: 2>)\n",
455 "notion (True, 17, 18, <Direction.up: 3>)\n",
456 "ogled (True, 1, 18, <Direction.down: 4>)\n",
457 "originality (False, 0, 0, <Direction.left: 1>)\n",
458 "outings (False, 0, 0, <Direction.left: 1>)\n",
459 "pendulous (False, 0, 0, <Direction.left: 1>)\n",
460 "piled (True, 1, 10, <Direction.right: 2>)\n",
461 "pins (True, 7, 4, <Direction.upleft: 5>)\n",
462 "pithier (False, 0, 0, <Direction.left: 1>)\n",
463 "prep (True, 10, 4, <Direction.right: 2>)\n",
464 "randomness (False, 0, 0, <Direction.left: 1>)\n",
465 "rectors (False, 0, 0, <Direction.left: 1>)\n",
466 "redrew (False, 0, 0, <Direction.left: 1>)\n",
467 "reformulated (False, 0, 0, <Direction.left: 1>)\n",
468 "remoteness (False, 0, 0, <Direction.left: 1>)\n",
469 "retaking (True, 6, 0, <Direction.down: 4>)\n",
470 "rethink (False, 0, 0, <Direction.left: 1>)\n",
471 "rope (True, 9, 4, <Direction.right: 2>)\n",
472 "rubier (True, 0, 4, <Direction.downright: 8>)\n",
473 "sailors (True, 7, 15, <Direction.up: 3>)\n",
474 "scowls (False, 0, 0, <Direction.left: 1>)\n",
475 "scum (True, 16, 11, <Direction.right: 2>)\n",
476 "sepals (True, 6, 10, <Direction.upright: 6>)\n",
477 "sequencers (False, 0, 0, <Direction.left: 1>)\n",
478 "serf (False, 0, 0, <Direction.left: 1>)\n",
479 "shoaled (True, 11, 18, <Direction.up: 3>)\n",
480 "shook (False, 0, 0, <Direction.left: 1>)\n",
481 "sonic (True, 18, 18, <Direction.left: 1>)\n",
482 "spottiest (False, 0, 0, <Direction.left: 1>)\n",
483 "stag (True, 7, 8, <Direction.left: 1>)\n",
484 "stood (False, 0, 0, <Direction.left: 1>)\n",
485 "stratum (True, 2, 13, <Direction.left: 1>)\n",
486 "strong (True, 4, 19, <Direction.down: 4>)\n",
487 "studying (True, 0, 16, <Direction.left: 1>)\n",
488 "surtaxing (False, 0, 0, <Direction.left: 1>)\n",
489 "tailing (True, 13, 6, <Direction.right: 2>)\n",
490 "tears (True, 13, 3, <Direction.up: 3>)\n",
491 "teazles (True, 4, 10, <Direction.downright: 8>)\n",
492 "vans (True, 18, 8, <Direction.upright: 6>)\n",
493 "wardrobes (False, 0, 0, <Direction.left: 1>)\n",
494 "wooded (True, 12, 5, <Direction.right: 2>)\n",
495 "worsts (True, 1, 0, <Direction.downright: 8>)\n",
496 "zings (True, 10, 14, <Direction.upleft: 5>)\n"
502 " print(w, present(g, w))"
506 "cell_type": "markdown",
509 "Which words are present?"
514 "execution_count": 69,
521 "output_type": "stream",
523 "1 loop, best of 3: 1.28 s per loop\n"
529 "[w for w in ws if present(g, w)[0]]"
534 "execution_count": 70,
541 "output_type": "stream",
543 "1 loop, best of 3: 215 ms per loop\n"
549 "[p[0] for p in present_many(g, ws)]"
554 "execution_count": 61,
588 " 'fortification',\n",
597 " 'hopelessness',\n",
605 " 'justification',\n",
632 " 'reformulated',\n",
664 "execution_count": 61,
666 "output_type": "execute_result"
674 "cell_type": "markdown",
677 "What is the longest word that is present?"
682 "execution_count": 15,
691 "execution_count": 15,
693 "output_type": "execute_result"
697 "sorted([w for w in ws if present(g, w)[0]], key=len)[-1]"
701 "cell_type": "markdown",
704 "What is the longest word that is absent?"
709 "execution_count": 16,
718 "execution_count": 16,
720 "output_type": "execute_result"
724 "sorted([w for w in ws if not present(g, w)[0]], key=len)[-1]"
728 "cell_type": "markdown",
731 "How many letters are unused?"
736 "execution_count": 17,
745 "execution_count": 17,
747 "output_type": "execute_result"
751 "g0 = empty_grid(width, height)\n",
753 " p, r, c, d = present(g, w)\n",
755 " set_grid(g0, r, c, d, w)\n",
756 "len([c for c in cat(cat(l) for l in g0) if c == '.'])"
760 "cell_type": "markdown",
763 "What is the longest word you can make form the leftover letters?"
768 "execution_count": 18,
776 "Counter({'a': 4,\n",
798 "execution_count": 18,
800 "output_type": "execute_result"
804 "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",
806 "unused_letter_count = collections.Counter(unused_letters)\n",
807 "unused_letter_count"
812 "execution_count": 19,
833 " 'hopelessness',\n",
840 " 'justification',\n",
851 " 'reformulated',\n",
864 "execution_count": 19,
866 "output_type": "execute_result"
870 "unused_words = [w for w in ws if not present(g, w)[0]]\n",
876 "execution_count": 20,
883 "output_type": "stream",
885 "ancestor Counter({'t': 1, 'r': 1, 'a': 1, 'e': 1, 'o': 1, 's': 1, 'n': 1, 'c': 1})\n",
886 "baritone Counter({'t': 1, 'i': 1, 'a': 1, 'e': 1, 'n': 1, 'o': 1, 'r': 1, 'b': 1})\n",
887 "bemusing Counter({'m': 1, 'i': 1, 'n': 1, 'g': 1, 'e': 1, 's': 1, 'u': 1, 'b': 1})\n",
888 "blonds Counter({'n': 1, 's': 1, 'd': 1, 'l': 1, 'o': 1, 'b': 1})\n",
889 "conciseness Counter({'s': 3, 'n': 2, 'e': 2, 'c': 2, 'i': 1, 'o': 1})\n",
890 "consequent Counter({'n': 2, 'e': 2, 't': 1, 'q': 1, 's': 1, 'u': 1, 'o': 1, 'c': 1})\n",
891 "cuddle Counter({'d': 2, 'l': 1, 'u': 1, 'e': 1, 'c': 1})\n",
892 "dashboards Counter({'a': 2, 'd': 2, 's': 2, 'r': 1, 'h': 1, 'o': 1, 'b': 1})\n",
893 "*despairing Counter({'i': 2, 'a': 1, 'g': 1, 'e': 1, 'n': 1, 'r': 1, 'd': 1, 'p': 1, 's': 1})\n",
894 "dint Counter({'t': 1, 'i': 1, 'd': 1, 'n': 1})\n",
895 "employer Counter({'e': 2, 'm': 1, 'r': 1, 'p': 1, 'o': 1, 'l': 1, 'y': 1})\n",
896 "freakish Counter({'i': 1, 'a': 1, 'h': 1, 'e': 1, 's': 1, 'r': 1, 'f': 1, 'k': 1})\n",
897 "*gall Counter({'l': 2, 'a': 1, 'g': 1})\n",
898 "hopelessness Counter({'s': 4, 'e': 3, 'n': 1, 'h': 1, 'l': 1, 'o': 1, 'p': 1})\n",
899 "*impales Counter({'m': 1, 'i': 1, 'a': 1, 'e': 1, 'p': 1, 's': 1, 'l': 1})\n",
900 "infix Counter({'i': 2, 'x': 1, 'n': 1, 'f': 1})\n",
901 "inflow Counter({'i': 1, 'n': 1, 'w': 1, 'o': 1, 'f': 1, 'l': 1})\n",
902 "innumerable Counter({'n': 2, 'e': 2, 'm': 1, 'i': 1, 'l': 1, 'r': 1, 'u': 1, 'a': 1, 'b': 1})\n",
903 "intentional Counter({'n': 3, 't': 2, 'i': 2, 'e': 1, 'o': 1, 'l': 1, 'a': 1})\n",
904 "*jerkin Counter({'i': 1, 'n': 1, 'e': 1, 'r': 1, 'j': 1, 'k': 1})\n",
905 "justification Counter({'i': 3, 't': 2, 'a': 1, 'n': 1, 'j': 1, 's': 1, 'f': 1, 'u': 1, 'o': 1, 'c': 1})\n",
906 "leaving Counter({'i': 1, 'a': 1, 'v': 1, 'e': 1, 'l': 1, 'n': 1, 'g': 1})\n",
907 "locoweeds Counter({'e': 2, 'o': 2, 's': 1, 'w': 1, 'l': 1, 'd': 1, 'c': 1})\n",
908 "monickers Counter({'m': 1, 'i': 1, 'n': 1, 'e': 1, 's': 1, 'r': 1, 'o': 1, 'k': 1, 'c': 1})\n",
909 "originality Counter({'i': 3, 't': 1, 'n': 1, 'g': 1, 'y': 1, 'o': 1, 'a': 1, 'l': 1, 'r': 1})\n",
910 "outings Counter({'t': 1, 'i': 1, 'n': 1, 'g': 1, 'o': 1, 'u': 1, 's': 1})\n",
911 "pendulous Counter({'u': 2, 'n': 1, 'l': 1, 'e': 1, 's': 1, 'p': 1, 'd': 1, 'o': 1})\n",
912 "pithier Counter({'i': 2, 't': 1, 'h': 1, 'e': 1, 'r': 1, 'p': 1})\n",
913 "randomness Counter({'n': 2, 's': 2, 'm': 1, 'a': 1, 'e': 1, 'r': 1, 'o': 1, 'd': 1})\n",
914 "rectors Counter({'r': 2, 't': 1, 'e': 1, 's': 1, 'o': 1, 'c': 1})\n",
915 "redrew Counter({'r': 2, 'e': 2, 'w': 1, 'd': 1})\n",
916 "reformulated Counter({'e': 2, 'r': 2, 'm': 1, 't': 1, 'a': 1, 'd': 1, 'l': 1, 'f': 1, 'u': 1, 'o': 1})\n",
917 "remoteness Counter({'e': 3, 's': 2, 'm': 1, 't': 1, 'n': 1, 'r': 1, 'o': 1})\n",
918 "rethink Counter({'t': 1, 'i': 1, 'n': 1, 'h': 1, 'e': 1, 'r': 1, 'k': 1})\n",
919 "scowls Counter({'s': 2, 'w': 1, 'l': 1, 'o': 1, 'c': 1})\n",
920 "sequencers Counter({'e': 3, 's': 2, 'n': 1, 'q': 1, 'u': 1, 'r': 1, 'c': 1})\n",
921 "serf Counter({'s': 1, 'f': 1, 'r': 1, 'e': 1})\n",
922 "shook Counter({'o': 2, 'k': 1, 's': 1, 'h': 1})\n",
923 "spottiest Counter({'t': 3, 's': 2, 'i': 1, 'e': 1, 'p': 1, 'o': 1})\n",
924 "stood Counter({'o': 2, 't': 1, 'd': 1, 's': 1})\n",
925 "surtaxing Counter({'t': 1, 'x': 1, 'a': 1, 'g': 1, 'i': 1, 'n': 1, 's': 1, 'u': 1, 'r': 1})\n",
926 "wardrobes Counter({'r': 2, 'o': 1, 'a': 1, 'e': 1, 's': 1, 'w': 1, 'd': 1, 'b': 1})\n"
931 "makeable_words = []\n",
932 "for w in unused_words:\n",
933 " unused_word_count = collections.Counter(w)\n",
934 " if all(unused_word_count[l] <= unused_letter_count[l] for l in unused_word_count):\n",
935 " makeable_words += [w]\n",
936 " print('*', end='')\n",
937 " print(w, unused_word_count)"
942 "execution_count": 21,
951 "execution_count": 21,
953 "output_type": "execute_result"
957 "max(len(w) for w in makeable_words)"
962 "execution_count": 22,
971 "execution_count": 22,
973 "output_type": "execute_result"
977 "sorted(makeable_words, key=len)[-1]"
982 "execution_count": 78,
988 "def do_wordsearch_tasks_old(fn, show_anyway=False):\n",
989 " width, height, grid, words = read_wordsearch(fn)\n",
991 " used_words = [w for w in words if present(grid, w)[0]]\n",
992 " unused_words = [w for w in words if not present(grid, w)[0]]\n",
993 " lwp = sorted([w for w in words if present(grid, w)[0]], key=len)[-1]\n",
994 " lwa = sorted(unused_words, key=len)[-1]\n",
996 " lwps = [w for w in used_words if len(w) == len(lwp)]\n",
997 " lwas = [w for w in unused_words if len(w) == len(lwa)]\n",
998 " g0 = empty_grid(width, height)\n",
999 " for w in words:\n",
1000 " p, r, c, d = present(grid, w)\n",
1002 " set_grid(g0, r, c, d, w) \n",
1003 " 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",
1005 " unused_letter_count = collections.Counter(unused_letters)\n",
1006 " makeable_words = []\n",
1007 " for w in unused_words:\n",
1008 " unused_word_count = collections.Counter(w)\n",
1009 " if all(unused_word_count[l] <= unused_letter_count[l] for l in unused_word_count):\n",
1010 " makeable_words += [w]\n",
1011 " lwm = sorted(makeable_words, key=len)[-1]\n",
1012 " lwms = [w for w in makeable_words if len(w) == len(lwm)]\n",
1013 " if show_anyway or len(set((len(lwp),len(lwa),len(lwm)))) == 3:\n",
1014 " print('\\n{}'.format(fn))\n",
1015 " print('{} words present'.format(len(words) - len(unused_words)))\n",
1016 " print('Longest word present: {}, {} letters ({})'.format(lwp, len(lwp), lwps))\n",
1017 " print('Longest word absent: {}, {} letters ({})'.format(lwa, len(lwa), lwas))\n",
1018 " print('{} unused letters'.format(len([c for c in cat(cat(l) for l in g0) if c == '.'])))\n",
1019 " print('Longest makeable word: {}, {} ({})'.format(lwm, len(lwm), lwms))\n",
1020 " print('All makeable words: {}'.format(makeable_words))"
1024 "cell_type": "code",
1025 "execution_count": 152,
1031 "def do_wordsearch_tasks(fn, show_anyway=False, show_all_makeable=False):\n",
1032 " width, height, grid, words = read_wordsearch(fn)\n",
1033 " used_words = [p[0] for p in present_many(grid, words)]\n",
1034 " unused_words = [w for w in words if w not in used_words]\n",
1035 " lwp = max(used_words, key=len)\n",
1036 " lwps = [w for w in used_words if len(w) == len(lwp)]\n",
1038 " if unused_words:\n",
1039 " lwa = max(unused_words, key=len)\n",
1041 " lwas = [w for w in unused_words if len(w) == len(lwa)]\n",
1042 " unused_grid = [[c for c in r] for r in grid]\n",
1043 " for w, r, c, d in present_many(grid, words):\n",
1044 " set_grid(unused_grid, r, c, d, '.' * len(w))\n",
1045 " unused_letters = [c for l in unused_grid for c in l if c != '.']\n",
1046 " unused_letter_count = collections.Counter(unused_letters)\n",
1047 " makeable_words = []\n",
1048 " for w in unused_words:\n",
1049 " unused_word_count = collections.Counter(w)\n",
1050 " if all(unused_word_count[l] <= unused_letter_count[l] for l in unused_word_count):\n",
1051 " makeable_words += [w]\n",
1052 " lwm = sorted(makeable_words, key=len)[-1]\n",
1053 " lwms = [w for w in makeable_words if len(w) == len(lwm)]\n",
1060 " if show_anyway or len(set((len(lwp),len(lwa),len(lwm)))) == 3:\n",
1061 " print('\\n{}'.format(fn))\n",
1062 " print('{} words present'.format(len(words) - len(unused_words)))\n",
1063 " print('Longest word present: {}, {} letters ({})'.format(lwp, len(lwp), lwps))\n",
1064 " print('Longest word absent: {}, {} letters ({})'.format(lwa, len(lwa), lwas))\n",
1065 " print('{} unused letters'.format(len([c for c in cat(cat(l) for l in g0) if c == '.'])))\n",
1066 " print('Longest makeable word: {}, {} ({})'.format(lwm, len(lwm), lwms))\n",
1067 " if show_all_makeable:\n",
1068 " print('All makeable words: {}'.format(makeable_words))"
1072 "cell_type": "code",
1073 "execution_count": 136,
1078 "output_type": "stream",
1081 "wordsearch04.txt\n",
1082 "58 words present\n",
1083 "Longest word present: fortification, 13 letters (['fortification'])\n",
1084 "Longest word absent: justification, 13 letters (['justification'])\n",
1085 "57 unused letters\n",
1086 "Longest makeable word: despairing, 10 (['despairing'])\n",
1087 "All makeable words: ['despairing', 'gall', 'impales', 'jerkin']\n"
1092 "do_wordsearch_tasks('wordsearch04.txt', show_anyway=True)"
1096 "cell_type": "code",
1097 "execution_count": 25,
1102 "output_type": "stream",
1105 "wordsearch08.txt\n",
1106 "62 words present\n",
1107 "Longest word present: compassionately, 15 letters (['compassionately'])\n",
1108 "Longest word absent: retrospectives, 14 letters (['retrospectives'])\n",
1109 "65 unused letters\n",
1110 "Longest makeable word: vacationing, 11 (['vacationing'])\n"
1115 "do_wordsearch_tasks('wordsearch08.txt')"
1119 "cell_type": "code",
1120 "execution_count": 80,
1125 "output_type": "stream",
1128 "04-wordsearch.txt\n",
1129 "62 words present\n",
1130 "Longest word present: compassionately, 15 letters (['compassionately'])\n",
1131 "Longest word absent: retrospectives, 14 letters (['retrospectives'])\n",
1132 "65 unused letters\n",
1133 "Longest makeable word: vacationing, 11 (['vacationing'])\n",
1134 "All makeable words: ['albacore', 'entail', 'excreted', 'factory', 'fornicated', 'frantic', 'grease', 'grocer', 'informal', 'jockeying', 'justified', 'larynxes', 'reviewer', 'sabotage', 'unable', 'vacationing', 'widgeons']\n",
1136 "04-wordsearch.txt\n",
1137 "62 words present\n",
1138 "Longest word present: compassionately, 15 letters (['compassionately'])\n",
1139 "Longest word absent: retrospectives, 14 letters (['retrospectives'])\n",
1140 "65 unused letters\n",
1141 "Longest makeable word: vacationing, 11 (['vacationing'])\n",
1142 "All makeable words: ['albacore', 'entail', 'excreted', 'factory', 'fornicated', 'frantic', 'grease', 'grocer', 'informal', 'jockeying', 'justified', 'larynxes', 'reviewer', 'sabotage', 'unable', 'vacationing', 'widgeons']\n",
1144 "04-wordsearch.txt\n",
1145 "62 words present\n",
1146 "Longest word present: compassionately, 15 letters (['compassionately'])\n",
1147 "Longest word absent: retrospectives, 14 letters (['retrospectives'])\n",
1148 "65 unused letters\n",
1149 "Longest makeable word: vacationing, 11 (['vacationing'])\n",
1150 "All makeable words: ['albacore', 'entail', 'excreted', 'factory', 'fornicated', 'frantic', 'grease', 'grocer', 'informal', 'jockeying', 'justified', 'larynxes', 'reviewer', 'sabotage', 'unable', 'vacationing', 'widgeons']\n",
1152 "04-wordsearch.txt\n",
1153 "62 words present\n",
1154 "Longest word present: compassionately, 15 letters (['compassionately'])\n",
1155 "Longest word absent: retrospectives, 14 letters (['retrospectives'])\n",
1156 "65 unused letters\n",
1157 "Longest makeable word: vacationing, 11 (['vacationing'])\n",
1158 "All makeable words: ['albacore', 'entail', 'excreted', 'factory', 'fornicated', 'frantic', 'grease', 'grocer', 'informal', 'jockeying', 'justified', 'larynxes', 'reviewer', 'sabotage', 'unable', 'vacationing', 'widgeons']\n",
1159 "1 loop, best of 3: 4.78 s per loop\n"
1165 "do_wordsearch_tasks_old('04-wordsearch.txt')"
1169 "cell_type": "code",
1170 "execution_count": 142,
1175 "output_type": "stream",
1177 "1 loop, best of 3: 463 ms per loop\n"
1183 "do_wordsearch_tasks('wordsearch04.txt')"
1187 "cell_type": "code",
1188 "execution_count": 143,
1193 "output_type": "stream",
1196 "example-wordsearch.txt\n",
1197 "14 words present\n",
1198 "Longest word present: cowhides, 8 letters (['cowhides'])\n",
1199 "Longest word absent: adapting, 8 letters (['adapting', 'coccyxes', 'crumbled', 'molested'])\n",
1200 "57 unused letters\n",
1201 "Longest makeable word: shinier, 7 (['shinier'])\n",
1202 "All makeable words: ['shinier']\n"
1207 "do_wordsearch_tasks('example-wordsearch.txt', show_anyway=True)"
1211 "cell_type": "code",
1212 "execution_count": 26,
1219 "# for fn in sorted(os.listdir()):\n",
1220 "# if re.match('wordsearch\\d\\d\\.txt', fn):\n",
1221 "# do_wordsearch_tasks(fn)"
1225 "cell_type": "code",
1226 "execution_count": 27,
1233 "output_type": "stream",
1235 "absolved (True, 16, 2, <Direction.right: 2>)\n",
1236 "adorable (True, 11, 4, <Direction.right: 2>)\n",
1237 "aeon (True, 11, 4, <Direction.down: 4>)\n",
1238 "alias (True, 15, 15, <Direction.left: 1>)\n",
1239 "ancestor (False, 0, 0, <Direction.left: 1>)\n",
1240 "baritone (False, 0, 0, <Direction.left: 1>)\n",
1241 "bemusing (False, 0, 0, <Direction.left: 1>)\n",
1242 "blonds (False, 0, 0, <Direction.left: 1>)\n",
1243 "bran (True, 1, 9, <Direction.left: 1>)\n",
1244 "calcite (True, 19, 9, <Direction.upright: 6>)\n",
1245 "candor (True, 17, 12, <Direction.right: 2>)\n",
1246 "conciseness (False, 0, 0, <Direction.left: 1>)\n",
1247 "consequent (False, 0, 0, <Direction.left: 1>)\n",
1248 "cuddle (False, 0, 0, <Direction.left: 1>)\n",
1249 "damming (True, 17, 2, <Direction.right: 2>)\n",
1250 "dashboards (False, 0, 0, <Direction.left: 1>)\n",
1251 "despairing (False, 0, 0, <Direction.left: 1>)\n",
1252 "dint (False, 0, 0, <Direction.left: 1>)\n",
1253 "dullard (True, 8, 2, <Direction.down: 4>)\n",
1254 "dynasty (True, 3, 4, <Direction.downright: 8>)\n",
1255 "employer (False, 0, 0, <Direction.left: 1>)\n",
1256 "exhorts (True, 0, 8, <Direction.left: 1>)\n",
1257 "feted (True, 5, 10, <Direction.right: 2>)\n",
1258 "fill (True, 9, 14, <Direction.upleft: 5>)\n",
1259 "flattens (True, 10, 10, <Direction.upleft: 5>)\n",
1260 "foghorn (True, 10, 11, <Direction.downright: 8>)\n",
1261 "fortification (True, 19, 16, <Direction.left: 1>)\n",
1262 "freakish (False, 0, 0, <Direction.left: 1>)\n",
1263 "frolics (True, 16, 16, <Direction.up: 3>)\n",
1264 "gall (False, 0, 0, <Direction.left: 1>)\n",
1265 "gees (True, 17, 0, <Direction.upright: 6>)\n",
1266 "genies (True, 5, 7, <Direction.upleft: 5>)\n",
1267 "gets (True, 6, 4, <Direction.upleft: 5>)\n",
1268 "hastening (True, 14, 13, <Direction.left: 1>)\n",
1269 "hits (True, 2, 0, <Direction.down: 4>)\n",
1270 "hopelessness (False, 0, 0, <Direction.left: 1>)\n",
1271 "hurlers (True, 18, 0, <Direction.right: 2>)\n",
1272 "impales (False, 0, 0, <Direction.left: 1>)\n",
1273 "infix (False, 0, 0, <Direction.left: 1>)\n",
1274 "inflow (False, 0, 0, <Direction.left: 1>)\n",
1275 "innumerable (False, 0, 0, <Direction.left: 1>)\n",
1276 "intentional (False, 0, 0, <Direction.left: 1>)\n",
1277 "jerkin (False, 0, 0, <Direction.left: 1>)\n",
1278 "justification (False, 0, 0, <Direction.left: 1>)\n",
1279 "kitty (True, 8, 15, <Direction.upleft: 5>)\n",
1280 "knuckles (True, 17, 19, <Direction.up: 3>)\n",
1281 "leaving (False, 0, 0, <Direction.left: 1>)\n",
1282 "like (True, 3, 11, <Direction.left: 1>)\n",
1283 "limitation (True, 8, 3, <Direction.right: 2>)\n",
1284 "locoweeds (False, 0, 0, <Direction.left: 1>)\n",
1285 "loot (True, 3, 19, <Direction.up: 3>)\n",
1286 "lucking (True, 7, 10, <Direction.upleft: 5>)\n",
1287 "lumps (True, 0, 17, <Direction.down: 4>)\n",
1288 "mercerising (True, 15, 17, <Direction.up: 3>)\n",
1289 "monickers (False, 0, 0, <Direction.left: 1>)\n",
1290 "motionless (True, 13, 1, <Direction.up: 3>)\n",
1291 "naturally (True, 9, 16, <Direction.up: 3>)\n",
1292 "nighest (True, 15, 4, <Direction.right: 2>)\n",
1293 "notion (True, 17, 18, <Direction.up: 3>)\n",
1294 "ogled (True, 1, 18, <Direction.down: 4>)\n",
1295 "originality (False, 0, 0, <Direction.left: 1>)\n",
1296 "outings (False, 0, 0, <Direction.left: 1>)\n",
1297 "pendulous (False, 0, 0, <Direction.left: 1>)\n",
1298 "piled (True, 1, 10, <Direction.right: 2>)\n",
1299 "pins (True, 7, 4, <Direction.upleft: 5>)\n",
1300 "pithier (False, 0, 0, <Direction.left: 1>)\n",
1301 "prep (True, 10, 4, <Direction.right: 2>)\n",
1302 "randomness (False, 0, 0, <Direction.left: 1>)\n",
1303 "rectors (False, 0, 0, <Direction.left: 1>)\n",
1304 "redrew (False, 0, 0, <Direction.left: 1>)\n",
1305 "reformulated (False, 0, 0, <Direction.left: 1>)\n",
1306 "remoteness (False, 0, 0, <Direction.left: 1>)\n",
1307 "retaking (True, 6, 0, <Direction.down: 4>)\n",
1308 "rethink (False, 0, 0, <Direction.left: 1>)\n",
1309 "rope (True, 9, 4, <Direction.right: 2>)\n",
1310 "rubier (True, 0, 4, <Direction.downright: 8>)\n",
1311 "sailors (True, 7, 15, <Direction.up: 3>)\n",
1312 "scowls (False, 0, 0, <Direction.left: 1>)\n",
1313 "scum (True, 16, 11, <Direction.right: 2>)\n",
1314 "sepals (True, 6, 10, <Direction.upright: 6>)\n",
1315 "sequencers (False, 0, 0, <Direction.left: 1>)\n",
1316 "serf (False, 0, 0, <Direction.left: 1>)\n",
1317 "shoaled (True, 11, 18, <Direction.up: 3>)\n",
1318 "shook (False, 0, 0, <Direction.left: 1>)\n",
1319 "sonic (True, 18, 18, <Direction.left: 1>)\n",
1320 "spottiest (False, 0, 0, <Direction.left: 1>)\n",
1321 "stag (True, 7, 8, <Direction.left: 1>)\n",
1322 "stood (False, 0, 0, <Direction.left: 1>)\n",
1323 "stratum (True, 2, 13, <Direction.left: 1>)\n",
1324 "strong (True, 4, 19, <Direction.down: 4>)\n",
1325 "studying (True, 0, 16, <Direction.left: 1>)\n",
1326 "surtaxing (False, 0, 0, <Direction.left: 1>)\n",
1327 "tailing (True, 13, 6, <Direction.right: 2>)\n",
1328 "tears (True, 13, 3, <Direction.up: 3>)\n",
1329 "teazles (True, 4, 10, <Direction.downright: 8>)\n",
1330 "vans (True, 18, 8, <Direction.upright: 6>)\n",
1331 "wardrobes (False, 0, 0, <Direction.left: 1>)\n",
1332 "wooded (True, 12, 5, <Direction.right: 2>)\n",
1333 "worsts (True, 1, 0, <Direction.downright: 8>)\n",
1334 "zings (True, 10, 14, <Direction.upleft: 5>)\n"
1339 "width, height, grid, words = read_wordsearch('wordsearch04.txt')\n",
1340 "for w in words:\n",
1341 " print(w, present(grid, w))"
1345 "cell_type": "markdown",
1348 "## Example for question text"
1352 "cell_type": "code",
1353 "execution_count": 28,
1360 "grid = [['.', '.', '.', 'p', '.', 'm', 'o', 'w', 'n', '.'], ['.', 's', 'd', 's', 'e', '.', '.', 'e', 'e', '.'], ['.', 'e', '.', 'e', 'l', 'a', 'd', '.', 'c', 'r'], ['p', 'i', '.', 'd', 't', 'i', 'r', '.', 'a', 'h'], ['r', 'z', 's', 'i', 'w', 'o', 'v', 's', 'p', 'u'], ['o', 'a', 'w', 'h', '.', 'k', 'i', 'e', 'a', 'b'], ['b', 'r', 'o', 'w', '.', 'c', '.', 'r', 'd', 'a'], ['e', 'c', 'n', 'o', 't', 'o', 'p', 's', '.', 'r'], ['d', '.', 'k', 'c', '.', 'd', '.', '.', '.', 'b'], ['.', 's', 't', 'a', 'p', 'l', 'e', '.', '.', '.']]\n",
1361 "padded_grid = [['f', 'h', 'j', 'p', 'a', 'm', 'o', 'w', 'n', 'q'], ['w', 's', 'd', 's', 'e', 'u', 'q', 'e', 'e', 'v'], ['i', 'e', 'a', 'e', 'l', 'a', 'd', 'h', 'c', 'r'], ['p', 'i', 'e', 'd', 't', 'i', 'r', 'i', 'a', 'h'], ['r', 'z', 's', 'i', 'w', 'o', 'v', 's', 'p', 'u'], ['o', 'a', 'w', 'h', 'a', 'k', 'i', 'e', 'a', 'b'], ['b', 'r', 'o', 'w', 'p', 'c', 'f', 'r', 'd', 'a'], ['e', 'c', 'n', 'o', 't', 'o', 'p', 's', 's', 'r'], ['d', 'i', 'k', 'c', 'h', 'd', 'n', 'p', 'n', 'b'], ['b', 's', 't', 'a', 'p', 'l', 'e', 'o', 'k', 'r']]\n",
1362 "present_words = ['probed', 'staple', 'rioted', 'cowhides', 'tops', 'knows', 'lived', 'rhubarb', 'crazies', 'dock', 'apace', 'mown', 'pears', 'wide']\n",
1363 "decoy_words = ['fickler', 'adapting', 'chump', 'foaming', 'molested', 'carnal', 'crumbled', 'guts', 'minuend', 'bombing', 'winced', 'coccyxes', 'solaria', 'shinier', 'cackles']"
1367 "cell_type": "code",
1368 "execution_count": 29,
1374 "'apace, cowhides, crazies, dock, knows, lived, mown, pears, probed, rhubarb, rioted, staple, tops, wide'"
1377 "execution_count": 29,
1379 "output_type": "execute_result"
1383 "', '.join(sorted(present_words))"
1387 "cell_type": "code",
1388 "execution_count": 92,
1397 "execution_count": 92,
1399 "output_type": "execute_result"
1403 "sum(len(w) for w in present_words), len(present_words)"
1407 "cell_type": "code",
1408 "execution_count": 30,
1414 "'adapting, bombing, cackles, carnal, chump, coccyxes, crumbled, fickler, foaming, guts, minuend, molested, shinier, solaria, winced'"
1417 "execution_count": 30,
1419 "output_type": "execute_result"
1423 "', '.join(sorted(decoy_words))"
1427 "cell_type": "code",
1428 "execution_count": 31,
1434 "'adapting, apace, bombing, cackles, carnal, chump, coccyxes, cowhides, crazies, crumbled, dock, fickler, foaming, guts, knows, lived, minuend, molested, mown, pears, probed, rhubarb, rioted, shinier, solaria, staple, tops, wide, winced'"
1437 "execution_count": 31,
1439 "output_type": "execute_result"
1443 "', '.join(sorted(present_words + decoy_words))"
1447 "cell_type": "code",
1448 "execution_count": 32,
1453 "output_type": "stream",
1455 "probed 3 0 6 Direction.down [(3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0)]\n",
1456 "staple 9 1 6 Direction.right [(9, 1), (9, 2), (9, 3), (9, 4), (9, 5), (9, 6)]\n",
1457 "rioted 6 7 6 Direction.upleft [(6, 7), (5, 6), (4, 5), (3, 4), (2, 3), (1, 2)]\n",
1458 "cowhides 8 3 8 Direction.up [(8, 3), (7, 3), (6, 3), (5, 3), (4, 3), (3, 3), (2, 3), (1, 3)]\n",
1459 "tops 7 4 4 Direction.right [(7, 4), (7, 5), (7, 6), (7, 7)]\n",
1460 "knows 8 2 5 Direction.up [(8, 2), (7, 2), (6, 2), (5, 2), (4, 2)]\n",
1461 "lived 2 4 5 Direction.downright [(2, 4), (3, 5), (4, 6), (5, 7), (6, 8)]\n",
1462 "rhubarb 2 9 7 Direction.down [(2, 9), (3, 9), (4, 9), (5, 9), (6, 9), (7, 9), (8, 9)]\n",
1463 "crazies 7 1 7 Direction.up [(7, 1), (6, 1), (5, 1), (4, 1), (3, 1), (2, 1), (1, 1)]\n",
1464 "dock 8 5 4 Direction.up [(8, 5), (7, 5), (6, 5), (5, 5)]\n",
1465 "apace 5 8 5 Direction.up [(5, 8), (4, 8), (3, 8), (2, 8), (1, 8)]\n",
1466 "mown 0 5 4 Direction.right [(0, 5), (0, 6), (0, 7), (0, 8)]\n",
1467 "pears 0 3 5 Direction.downright [(0, 3), (1, 4), (2, 5), (3, 6), (4, 7)]\n",
1468 "wide 4 4 4 Direction.upright [(4, 4), (3, 5), (2, 6), (1, 7)]\n"
1474 "[(7, 5), (2, 3), (3, 5)]"
1477 "execution_count": 32,
1479 "output_type": "execute_result"
1483 "cts = collections.Counter()\n",
1484 "for w in present_words:\n",
1485 " _, r, c, d = present(grid, w)\n",
1486 " inds = indices(grid, r, c, len(w), d)\n",
1487 " for i in inds:\n",
1489 " print(w, r, c, len(w), d, inds)\n",
1490 "[i for i in cts if cts[i] > 1]"
1494 "cell_type": "code",
1495 "execution_count": 33,
1500 "output_type": "stream",
1503 "example-wordsearch.txt\n",
1504 "14 words present\n",
1505 "Longest word present: cowhides, 8 letters (['cowhides'])\n",
1506 "Longest word absent: molested, 8 letters (['adapting', 'coccyxes', 'crumbled', 'molested'])\n",
1507 "27 unused letters\n",
1508 "Longest makeable word: shinier, 7 (['shinier'])\n"
1513 "do_wordsearch_tasks('example-wordsearch.txt', show_anyway=True)"
1517 "cell_type": "code",
1518 "execution_count": 34,
1523 "output_type": "stream",
1539 "g = empty_grid(10, 10)\n",
1540 "set_grid(g, 2, 4, Direction.downright, 'lived')\n",
1541 "print(show_grid(g))"
1545 "cell_type": "code",
1546 "execution_count": 35,
1551 "output_type": "stream",
1567 "g = empty_grid(10, 10)\n",
1568 "set_grid(g, 2, 4, Direction.downright, 'lived')\n",
1569 "set_grid(g, 4, 4, Direction.upright, 'wide')\n",
1570 "set_grid(g, 9, 1, Direction.right, 'staple')\n",
1571 "print(show_grid(g))"
1575 "cell_type": "code",
1576 "execution_count": 36,
1581 "output_type": "stream",
1597 "g = empty_grid(10, 10)\n",
1598 "set_grid(g, 8, 3, Direction.up, 'cowhides')\n",
1599 "print(show_grid(g))"
1603 "cell_type": "code",
1604 "execution_count": 39,
1609 "output_type": "stream",
1625 "g = empty_grid(10, 10)\n",
1626 "set_grid(g, 2, 4, Direction.downright, 'lived')\n",
1627 "set_grid(g, 4, 4, Direction.upright, 'wide')\n",
1628 "set_grid(g, 9, 1, Direction.right, 'staple')\n",
1629 "set_grid(g, 8, 3, Direction.up, 'cowhides')\n",
1630 "print(show_grid(g))"
1634 "cell_type": "code",
1635 "execution_count": 37,
1640 "output_type": "stream",
1656 "# Example of word in grid that is English but isn't in the words listed in the puzzle.\n",
1657 "g = empty_grid(10, 10)\n",
1658 "set_grid(g, 6, 0, Direction.right, 'brow')\n",
1659 "print(show_grid(g))"
1663 "cell_type": "code",
1664 "execution_count": 38,
1669 "output_type": "stream",
1686 "'aaabeffhhhiiijknnoppqqrsuvw'"
1689 "execution_count": 38,
1691 "output_type": "execute_result"
1695 "unused_grid = copy.deepcopy(padded_grid)\n",
1696 "for w in present_words:\n",
1697 " _, r, c, d = present(grid, w)\n",
1698 " set_grid(unused_grid, r, c, d, '.' * len(w))\n",
1699 "print(show_grid(unused_grid))\n",
1700 "cat(sorted(c for l in unused_grid for c in l if c in string.ascii_letters))"
1704 "cell_type": "code",
1705 "execution_count": 201,
1714 "execution_count": 201,
1716 "output_type": "execute_result"
1720 "len(sorted(c for l in unused_grid for c in l if c in 'aeiou'))"
1724 "cell_type": "markdown",
1727 "# All wordsearch puzzles"
1731 "cell_type": "code",
1732 "execution_count": 108,
1738 "def read_all_wordsearch(fn):\n",
1739 " with open(fn) as f:\n",
1740 " text = f.read().strip()\n",
1741 " puzzles_text = text.split('\\n\\n')\n",
1743 " for p in puzzles_text:\n",
1744 " lines = p.splitlines()\n",
1745 " w, h = [int(s) for s in lines[0].split('x')]\n",
1746 " grid = lines[1:h+1]\n",
1747 " words = lines[h+1:]\n",
1748 " puzzles += [(w, h, grid, words)]\n",
1753 "cell_type": "code",
1754 "execution_count": 109,
1762 " ['esaetarotcetorpwvnma',\n",
1763 " 'dhuejswellraqzassuzn',\n",
1764 " 'riopstewsidftvenlnlo',\n",
1765 " 'dltnpupodiafilgeenap',\n",
1766 " 'yeooooosvconsgatvenm',\n",
1767 " 'tgtonrsdtpgsungsireo',\n",
1768 " 'csmtnlaistdklisaeyrp',\n",
1769 " 'fguuusortrewfkrfdluo',\n",
1770 " 'dotcnpvoyiraicsrieht',\n",
1771 " 'drtcoataksogdaekcoyy',\n",
1772 " 'igoialcuneoneuasirvy',\n",
1773 " 'ajnzdpacoqrbsoshmgnt',\n",
1774 " 'mmsxeetcewussviipeei',\n",
1775 " 'esbrevrioprivilramsr',\n",
1776 " 'tsgerdvcvutesbtrrska',\n",
1777 " 'eyselimisapenheettcl',\n",
1778 " 'ryponacqcetsdsddiouu',\n",
1779 " 'streitsaotsedalaanlg',\n",
1780 " 'foretselppusdfrsleae',\n",
1781 " 'utsolacebeardingpher'],\n",
1782 " ['aboveboard',\n",
1785 " 'arbitrarily',\n",
1790 " 'bivouacking',\n",
1798 " 'depreciates',\n",
1806 " 'expurgations',\n",
1840 " 'overhearing',\n",
1842 " 'particularly',\n",
1847 " 'protectorate',\n",
1868 " 'suppositions',\n",
1884 "execution_count": 109,
1886 "output_type": "execute_result"
1890 "puzzles = read_all_wordsearch('all-wordsearches.txt')\n",
1895 "cell_type": "code",
1896 "execution_count": 110,
1902 "def found_words_length(puzzle):\n",
1903 " width, height, grid, words = puzzle\n",
1904 " return sum(len(p[0]) for p in present_many(grid, words))"
1908 "cell_type": "code",
1909 "execution_count": 113,
2019 "execution_count": 113,
2021 "output_type": "execute_result"
2025 "[found_words_length(p) for p in puzzles]"
2029 "cell_type": "code",
2030 "execution_count": 114,
2039 "execution_count": 114,
2041 "output_type": "execute_result"
2045 "sum(found_words_length(p) for p in puzzles)"
2049 "cell_type": "code",
2050 "execution_count": 122,
2056 "def max_unfound_word_length(puzzle):\n",
2057 " width, height, grid, words = puzzle\n",
2058 " presences = present_many(grid, words)\n",
2059 " used_words = [p[0] for p in presences]\n",
2060 " unused_words = [w for w in words if w not in used_words]\n",
2062 " unused_grid = [[c for c in r] for r in grid]\n",
2063 " for w, r, c, d in presences:\n",
2064 " set_grid(unused_grid, r, c, d, '.' * len(w))\n",
2065 " unused_letters = [c for l in unused_grid for c in l if c != '.']\n",
2066 " unused_letter_count = collections.Counter(unused_letters)\n",
2068 " makeable_words = []\n",
2069 " for w in unused_words:\n",
2070 " unused_word_count = collections.Counter(w)\n",
2071 " if all(unused_word_count[l] <= unused_letter_count[l] for l in unused_word_count):\n",
2072 " makeable_words += [w]\n",
2073 " lwm = max(len(w) for w in makeable_words)\n",
2078 "cell_type": "code",
2079 "execution_count": 123,
2189 "execution_count": 123,
2191 "output_type": "execute_result"
2195 "[max_unfound_word_length(p) for p in puzzles]"
2199 "cell_type": "code",
2200 "execution_count": 121,
2209 "execution_count": 121,
2211 "output_type": "execute_result"
2215 "sum(max_unfound_word_length(p) for p in puzzles)"
2219 "cell_type": "code",
2220 "execution_count": 124,
2225 "output_type": "stream",
2227 "1 loop, best of 3: 18.8 s per loop\n"
2233 "sum(found_words_length(p) for p in puzzles)"
2237 "cell_type": "code",
2238 "execution_count": 125,
2243 "output_type": "stream",
2245 "1 loop, best of 3: 18.7 s per loop\n"
2251 "sum(max_unfound_word_length(p) for p in puzzles)"
2255 "cell_type": "code",
2256 "execution_count": 190,
2263 "output_type": "stream",
2266 "huge-wordsearch.txt\n",
2267 "1149 words present\n",
2268 "Longest word present: hypersensitivities, 18 letters (['hypersensitivities'])\n",
2269 "Longest word absent: rambunctiousness, 16 letters (['rambunctiousness'])\n",
2270 "57 unused letters\n",
2271 "Longest makeable word: rambunctiousness, 16 (['rambunctiousness'])\n"
2276 "do_wordsearch_tasks('huge-wordsearch.txt', show_anyway=True)"
2280 "cell_type": "code",
2281 "execution_count": 191,
2286 "output_type": "stream",
2288 "1 loop, best of 3: 1min 4s per loop\n"
2294 "do_wordsearch_tasks('huge-wordsearch.txt')"
2298 "cell_type": "code",
2299 "execution_count": 192,
2305 "width, height, grid, words = read_wordsearch('huge-wordsearch.txt')\n",
2306 "pm = present_many(grid, words)\n",
2307 "pold = [w for w in words if present(grid, w)[0]]"
2311 "cell_type": "code",
2312 "execution_count": 193,
2321 "execution_count": 193,
2323 "output_type": "execute_result"
2331 "cell_type": "code",
2332 "execution_count": 194,
2341 "execution_count": 194,
2343 "output_type": "execute_result"
2351 "cell_type": "code",
2352 "execution_count": 195,
2361 "execution_count": 195,
2363 "output_type": "execute_result"
2367 "pm_extra = [p for p in pm if p not in pold]\n",
2372 "cell_type": "code",
2373 "execution_count": 196,
2379 "[('poltroons', 62, 65, <Direction.downleft: 7>),\n",
2380 " ('dogged', 7, 45, <Direction.down: 4>),\n",
2381 " ('activist', 51, 35, <Direction.downright: 8>)]"
2384 "execution_count": 196,
2386 "output_type": "execute_result"
2394 "cell_type": "code",
2395 "execution_count": 197,
2401 "['abound', 'abstracted', 'accidents']"
2404 "execution_count": 197,
2406 "output_type": "execute_result"
2414 "cell_type": "code",
2415 "execution_count": 198,
2421 "[('retreading', 1),\n",
2422 " ('disavows', 1),\n",
2423 " ('finals', 1),\n",
2424 " ('conniver', 1),\n",
2425 " ('warding', 1),\n",
2427 " ('paging', 1),\n",
2428 " ('booties', 1),\n",
2429 " ('civilises', 1),\n",
2433 "execution_count": 198,
2435 "output_type": "execute_result"
2439 "collections.Counter(p[0] for p in pm).most_common(10)"
2443 "cell_type": "code",
2444 "execution_count": 199,
2453 "execution_count": 199,
2455 "output_type": "execute_result"
2459 "[p for p in pm if p[0] == 'seen']"
2463 "cell_type": "code",
2464 "execution_count": null,
2474 "display_name": "Python 3",
2475 "language": "python",
2479 "codemirror_mode": {
2483 "file_extension": ".py",
2484 "mimetype": "text/x-python",
2486 "nbconvert_exporter": "python",
2487 "pygments_lexer": "ipython3",