12 "import collections\n",
15 "from enum import Enum\n",
16 "Direction = Enum('Direction', 'left right up down upleft upright downleft downright')\n",
18 "delta = {Direction.left: (0, -1),Direction.right: (0, 1), \n",
19 " Direction.up: (-1, 0), Direction.down: (1, 0), \n",
20 " Direction.upleft: (-1, -1), Direction.upright: (-1, 1), \n",
21 " Direction.downleft: (1, -1), Direction.downright: (1, 1)}\n",
34 "# all_words = [w.strip() for w in open('/usr/share/dict/british-english').readlines()\n",
35 "# if all(c in string.ascii_lowercase for c in w.strip())]\n",
36 "# words = [w for w in all_words\n",
37 "# if not any(w in w2 for w2 in all_words if w != w2)]\n",
38 "# open('wordsearch-words', 'w').write(lcat(words))"
47 "# ws_words = [w.strip() for w in open('wordsearch-words').readlines()\n",
48 "# if all(c in string.ascii_lowercase for c in w.strip())]\n",
60 "ws_words = [w.strip() for w in open('/usr/share/dict/british-english').readlines()\n",
61 " if all(c in string.ascii_lowercase for c in w.strip())]"
72 "def empty_grid(w, h):\n",
73 " return [['.' for c in range(w)] for r in range(h)]"
84 "def show_grid(grid):\n",
85 " return lcat(cat(r) for r in grid)"
95 "output_type": "stream",
111 "grid = empty_grid(10, 10)\n",
112 "print(show_grid(grid))"
117 "execution_count": 8,
123 "def indices(grid, r, c, l, d):\n",
124 " dr, dc = delta[d]\n",
125 " w = len(grid[0])\n",
127 " inds = [(r + i * dr, c + i * dc) for i in range(l)]\n",
128 " return [(i, j) for i, j in inds\n",
137 "execution_count": 9,
143 "def gslice(grid, r, c, l, d):\n",
144 " return [grid[i][j] for i, j in indices(grid, r, c, l, d)]"
149 "execution_count": 10,
155 "def set_grid(grid, r, c, d, word):\n",
156 " for (i, j), l in zip(indices(grid, r, c, len(word), d), word):\n",
163 "execution_count": 11,
168 "output_type": "stream",
184 "set_grid(grid, 2, 3, Direction.downright, 'testword')\n",
185 "print(show_grid(grid))"
190 "execution_count": 12,
199 "execution_count": 12,
201 "output_type": "execute_result"
205 "cat(gslice(grid, 3, 2, 15, Direction.right))"
210 "execution_count": 13,
216 "<_sre.SRE_Match object; span=(0, 4), match='keen'>"
219 "execution_count": 13,
221 "output_type": "execute_result"
225 "re.match(cat(gslice(grid, 3, 2, 4, Direction.right)), 'keen')"
230 "execution_count": 14,
236 "<_sre.SRE_Match object; span=(0, 3), match='kee'>"
239 "execution_count": 14,
241 "output_type": "execute_result"
245 "re.match(cat(gslice(grid, 3, 2, 3, Direction.right)), 'keen')"
250 "execution_count": 15,
254 "re.fullmatch(cat(gslice(grid, 3, 2, 3, Direction.right)), 'keen')"
259 "execution_count": 16,
265 "re.match(cat(gslice(grid, 3, 2, 4, Direction.right)), 'kine')"
270 "execution_count": 17,
276 "def could_add(grid, r, c, d, word):\n",
277 " s = gslice(grid, r, c, len(word), d)\n",
278 " return re.fullmatch(cat(s), word)"
283 "execution_count": 18,
289 "<_sre.SRE_Match object; span=(0, 4), match='keen'>"
292 "execution_count": 18,
294 "output_type": "execute_result"
298 "could_add(grid, 3, 2, Direction.right, 'keen')"
303 "execution_count": 19,
307 "could_add(grid, 3, 2, Direction.right, 'kine')"
312 "execution_count": 20,
318 "<Direction.right: 2>"
321 "execution_count": 20,
323 "output_type": "execute_result"
327 "random.choice(list(Direction))"
332 "execution_count": 58,
338 "def fill_grid(grid, words, word_count, max_attempts=10000):\n",
340 " added_words = []\n",
341 " w = len(grid[0])\n",
343 " while len(added_words) < word_count and attempts < max_attempts:\n",
345 " r = random.randrange(w)\n",
346 " c = random.randrange(h)\n",
347 " word = random.choice(words)\n",
348 " d = random.choice(list(Direction))\n",
349 " if len(word) >=4 and not any(word in w2 for w2 in added_words) and could_add(grid, r, c, d, word):\n",
350 " set_grid(grid, r, c, d, word)\n",
351 " added_words += [word]\n",
353 " return grid, added_words"
358 "execution_count": 22,
367 "execution_count": 22,
369 "output_type": "execute_result"
373 "g = empty_grid(20, 20)\n",
374 "g, ws = fill_grid(g, ws_words, 40)\n",
380 "execution_count": 23,
385 "output_type": "stream",
387 "..pouchexecrate.....\n",
388 ".hazardous....t.wive\n",
389 "...sniradnam.aselcnu\n",
390 "s.weeklies.rnb......\n",
391 "e..lamenessse.o.....\n",
392 "i.tsallab..s.a.i....\n",
393 "tslim.......f.c.l...\n",
394 "iwheelbase...f.tges.\n",
395 "ed....llabhgihinirr.\n",
396 "dw.limbs..nj.bitev.s\n",
397 "te.wiltediu.ructs.e.\n",
398 "elsombretv.oqes..a..\n",
399 "ll..e..te.iela....m.\n",
400 "ie.a..un.lheleiretoc\n",
401 "ord..bi.scsp...kiths\n",
402 "ts.malcentilitren...\n",
403 "..i.e.sexetrov.stolb\n",
404 ".r.s...ruof....htrof\n",
405 "bgnihsirevopmi.....c\n",
406 "....graciousness....\n",
408 "lameness impoverishing plasters wilted toilet forth coterie hazardous abutting chequing weeklies sombre execrate mastiffs boilers uncles centilitre mandarins wheelbase graciousness vortexes dwellers ballast limbs four tans highball wive broils beads mils reactive select deities shtik juveniles blots pouch brim coon\n"
413 "print(show_grid(g))\n",
414 "print(len(ws), 'words added')\n",
420 "execution_count": 24,
426 "def present(grid, word):\n",
427 " w = len(grid[0])\n",
429 " for r in range(h):\n",
430 " for c in range(w):\n",
431 " for d in Direction:\n",
432 " if cat(gslice(grid, r, c, len(word), d)) == word:\n",
433 " return True, r, c, d\n",
434 " return False, 0, 0, list(Direction)[0]"
439 "execution_count": 25,
446 "output_type": "stream",
448 "lameness (True, 4, 3, <Direction.right: 2>)\n",
449 "impoverishing (True, 18, 13, <Direction.left: 1>)\n",
450 "plasters (True, 14, 11, <Direction.upright: 6>)\n",
451 "wilted (True, 10, 3, <Direction.right: 2>)\n",
452 "toilet (True, 15, 0, <Direction.up: 3>)\n",
453 "forth (True, 17, 19, <Direction.left: 1>)\n",
454 "coterie (True, 13, 19, <Direction.left: 1>)\n",
455 "hazardous (True, 1, 1, <Direction.right: 2>)\n",
456 "abutting (True, 15, 4, <Direction.upright: 6>)\n",
457 "chequing (True, 14, 9, <Direction.upright: 6>)\n",
458 "weeklies (True, 3, 2, <Direction.right: 2>)\n",
459 "sombre (True, 11, 2, <Direction.right: 2>)\n",
460 "execrate (True, 0, 7, <Direction.right: 2>)\n",
461 "mastiffs (True, 12, 18, <Direction.upleft: 5>)\n",
462 "boilers (True, 3, 13, <Direction.downright: 8>)\n",
463 "uncles (True, 2, 19, <Direction.left: 1>)\n",
464 "centilitre (True, 15, 6, <Direction.right: 2>)\n",
465 "mandarins (True, 2, 11, <Direction.left: 1>)\n",
466 "wheelbase (True, 7, 1, <Direction.right: 2>)\n",
467 "graciousness (True, 19, 4, <Direction.right: 2>)\n",
468 "vortexes (True, 16, 13, <Direction.left: 1>)\n",
469 "dwellers (True, 8, 1, <Direction.down: 4>)\n",
470 "ballast (True, 5, 8, <Direction.left: 1>)\n",
471 "limbs (True, 9, 3, <Direction.right: 2>)\n",
472 "four (True, 17, 10, <Direction.left: 1>)\n",
473 "tans (True, 1, 14, <Direction.downleft: 7>)\n",
474 "highball (True, 8, 13, <Direction.left: 1>)\n",
475 "wive (True, 1, 16, <Direction.right: 2>)\n",
476 "broils (True, 9, 13, <Direction.downleft: 7>)\n",
477 "beads (True, 11, 5, <Direction.downleft: 7>)\n",
478 "mils (True, 6, 4, <Direction.left: 1>)\n",
479 "reactive (True, 3, 11, <Direction.downright: 8>)\n",
480 "select (True, 14, 10, <Direction.upright: 6>)\n",
481 "deities (True, 9, 0, <Direction.up: 3>)\n",
482 "shtik (True, 14, 19, <Direction.left: 1>)\n",
483 "juveniles (True, 9, 11, <Direction.downleft: 7>)\n",
484 "blots (True, 16, 19, <Direction.left: 1>)\n",
485 "pouch (True, 0, 2, <Direction.right: 2>)\n",
486 "brim (True, 18, 0, <Direction.upright: 6>)\n",
487 "coon (True, 18, 19, <Direction.upleft: 5>)\n"
493 " print(w, present(g, w))"
498 "execution_count": 47,
504 "def interesting(grid, words, words_limit=40, direction_slack=1):\n",
505 " dirs = set(present(grid, w)[3] for w in words)\n",
506 " return len(words) > words_limit and len(dirs) + direction_slack >= len(delta)"
511 "execution_count": 48,
520 "execution_count": 48,
522 "output_type": "execute_result"
531 "execution_count": 51,
537 "def interesting_grid(width=20, height=20, words_limit=40, direction_slack=1):\n",
540 " grid = empty_grid(width, height)\n",
541 " grid, words = fill_grid(grid, ws_words, 80)\n",
542 " boring = not interesting(grid, words, words_limit=words_limit, direction_slack=direction_slack)\n",
543 " return grid, words"
548 "execution_count": 52,
553 "output_type": "stream",
555 "dexawwt....ybossm..t\n",
556 "snaprhlukulele..o.er\n",
557 "...erioteb.hrevordla\n",
558 "..sivc..pr.tdekcahkp\n",
559 "atve..h.oiselapcy.cd\n",
560 "nesrehpargohtill.pue\n",
561 "n.leaveyis.smuguhhrt\n",
562 "eyhsifnt.r...aeesetc\n",
563 "xverbseebasedlritl.a\n",
564 ".r..k.wie..mdonaiaeg\n",
565 "..ac.nsof.admeeulsoo\n",
566 "g.opo.cmanaotpqiinmp\n",
567 "nc.us.a.lpnteaerdkiu\n",
568 "ionjoys.leirinioicns\n",
569 "wnkcent.skeracllkase\n",
570 "oab..erusopxeao.antt\n",
571 "dureifidimuhed.nskea\n",
572 "aga...capitols..scrl\n",
573 "h.v.sandblaster..i.i\n",
574 "s.e..sdratoelhitsn.d\n",
575 "61 words added; 8 directions\n",
576 "newscast kittenish cocks lithographers truckle leotards they exposure dehumidifier sandblaster alien paddle shadowing gondola wrest joys minster pales chairs fishy capitols based gums pheromones saki moiety waxed guano thriven dilate moray icons adman ukulele hacked rope rise clue acted nicknack spar verbs boss annex neck repeater befalls drover leave pans brave brigs opus live noun tail riot care hits quilt part\n"
581 "g, ws = interesting_grid()\n",
582 "print(show_grid(g))\n",
583 "print(len(ws), 'words added; ', len(set(present(g, w)[3] for w in ws)), 'directions')\n",
589 "execution_count": 30,
595 "def datafile(name, sep='\\t'):\n",
596 " \"\"\"Read key,value pairs from file.\n",
598 " with open(name) as f:\n",
600 " splits = line.split(sep)\n",
601 " yield [splits[0], int(splits[1])]"
606 "execution_count": 31,
612 "def normalise(frequencies):\n",
613 " \"\"\"Scale a set of frequencies so they sum to one\n",
615 " >>> sorted(normalise({1: 1, 2: 0}).items())\n",
616 " [(1, 1.0), (2, 0.0)]\n",
617 " >>> sorted(normalise({1: 1, 2: 1}).items())\n",
618 " [(1, 0.5), (2, 0.5)]\n",
619 " >>> sorted(normalise({1: 1, 2: 1, 3: 1}).items()) # doctest: +ELLIPSIS\n",
620 " [(1, 0.333...), (2, 0.333...), (3, 0.333...)]\n",
621 " >>> sorted(normalise({1: 1, 2: 2, 3: 1}).items())\n",
622 " [(1, 0.25), (2, 0.5), (3, 0.25)]\n",
624 " length = sum(f for f in frequencies.values())\n",
625 " return collections.defaultdict(int, ((k, v / length) \n",
626 " for (k, v) in frequencies.items()))\n"
631 "execution_count": 32,
635 "english_counts = collections.Counter(dict(datafile('count_1l.txt')))\n",
636 "normalised_english_counts = normalise(english_counts)"
641 "execution_count": 33,
645 "wordsearch_counts = collections.Counter(cat(ws_words))\n",
646 "normalised_wordsearch_counts = normalise(wordsearch_counts)"
651 "execution_count": 34,
655 "normalised_wordsearch_counts = normalise(collections.Counter(normalised_wordsearch_counts) + collections.Counter({l: 0.05 for l in string.ascii_lowercase}))"
660 "execution_count": 35,
666 "def weighted_choice(d):\n",
667 " \"\"\"Generate random item from a dictionary of item counts\n",
669 " target = random.uniform(0, sum(d.values()))\n",
671 " for (l, p) in d.items():\n",
673 " if cuml > target:\n",
677 "def random_english_letter():\n",
678 " \"\"\"Generate a random letter based on English letter counts\n",
680 " return weighted_choice(normalised_english_counts)\n",
682 "def random_wordsearch_letter():\n",
683 " \"\"\"Generate a random letter based on wordsearch letter counts\n",
685 " return weighted_choice(normalised_wordsearch_counts)"
690 "execution_count": 36,
696 "'aaaaaabbccddeeeeeeeeeeeffggghhhhhhiiiiiiiiillllmmnnnnnnnooooooooooppppqrrrrrrrssstttttttttuuwyyyyyyz'"
699 "execution_count": 36,
701 "output_type": "execute_result"
705 "cat(sorted(random_english_letter() for i in range(100)))"
710 "execution_count": 37,
716 "'aaaaaaaabbbccccddeeeeeeeeggghhhiiiiiiiijjkkllllnnnnoooooooooopppqqrrrrrssssssttttttuuuuvvwxxxxxyzzzz'"
719 "execution_count": 37,
721 "output_type": "execute_result"
725 "cat(sorted(random_wordsearch_letter() for i in range(100)))"
730 "execution_count": 38,
739 "execution_count": 38,
741 "output_type": "execute_result"
745 "random_wordsearch_letter()"
750 "execution_count": 39,
756 "def pad_grid(g0):\n",
757 " grid = copy.deepcopy(g0)\n",
758 " w = len(grid[0])\n",
760 " for r in range(h):\n",
761 " for c in range(w):\n",
762 " if grid[r][c] == '.':\n",
763 " grid[r][c] = random_wordsearch_letter()\n",
769 "execution_count": 53,
774 "output_type": "stream",
776 "dexawwtmxepybossmezt\n",
777 "snaprhlukulelefaoder\n",
778 "ijreriotebahrevordla\n",
779 "jrsivcciprstdekcahkp\n",
780 "atvecshkoiselapcydcd\n",
781 "nesrehpargohtilljpue\n",
782 "nuleaveyisasmuguhhrt\n",
783 "eyhsifntzrmnsaeesetc\n",
784 "xverbseebasedlritlla\n",
785 "prcckzwiefamdonaiaeg\n",
786 "osacqnsofgadmeeulsoo\n",
787 "gdoporcmanaotpqiinmp\n",
788 "ncmusbaslpnteaerdkiu\n",
789 "ionjoyswleirinioicns\n",
790 "wnkcentcskeracllkase\n",
791 "oabsuerusopxeaodantt\n",
792 "dureifidimuhedgnskea\n",
793 "agagvccapitolsgyscrl\n",
794 "hkvgsandblasterdhihi\n",
795 "syenesdratoelhitsnod\n"
800 "padded = pad_grid(g)\n",
801 "print(show_grid(padded))"
806 "execution_count": 54,
811 "output_type": "stream",
813 "dexawwt....ybossm..t\n",
814 "snaprhlukulele..o.er\n",
815 "...erioteb.hrevordla\n",
816 "..sivc..pr.tdekcahkp\n",
817 "atve..h.oiselapcy.cd\n",
818 "nesrehpargohtill.pue\n",
819 "n.leaveyis.smuguhhrt\n",
820 "eyhsifnt.r...aeesetc\n",
821 "xverbseebasedlritl.a\n",
822 ".r..k.wie..mdonaiaeg\n",
823 "..ac.nsof.admeeulsoo\n",
824 "g.opo.cmanaotpqiinmp\n",
825 "nc.us.a.lpnteaerdkiu\n",
826 "ionjoys.leirinioicns\n",
827 "wnkcent.skeracllkase\n",
828 "oab..erusopxeao.antt\n",
829 "dureifidimuhed.nskea\n",
830 "aga...capitols..scrl\n",
831 "h.v.sandblaster..i.i\n",
832 "s.e..sdratoelhitsn.d\n"
837 "print(show_grid(g))"
842 "execution_count": 55,
849 "output_type": "stream",
851 "newscast (True, 7, 6, <Direction.down: 4>)\n",
852 "kittenish (True, 14, 9, <Direction.upright: 6>)\n",
853 "cocks (True, 12, 1, <Direction.upright: 6>)\n",
854 "lithographers (True, 5, 14, <Direction.left: 1>)\n",
855 "truckle (True, 7, 18, <Direction.up: 3>)\n",
856 "leotards (True, 19, 12, <Direction.left: 1>)\n",
857 "they (True, 3, 11, <Direction.up: 3>)\n",
858 "exposure (True, 15, 12, <Direction.left: 1>)\n",
859 "dehumidifier (True, 16, 13, <Direction.left: 1>)\n",
860 "sandblaster (True, 18, 4, <Direction.right: 2>)\n",
861 "alien (True, 9, 17, <Direction.downleft: 7>)\n",
862 "paddle (True, 12, 9, <Direction.upright: 6>)\n",
863 "shadowing (True, 19, 0, <Direction.up: 3>)\n",
864 "gondola (True, 9, 19, <Direction.downleft: 7>)\n",
865 "wrest (True, 0, 5, <Direction.downleft: 7>)\n",
866 "joys (True, 13, 3, <Direction.right: 2>)\n",
867 "minster (True, 11, 18, <Direction.down: 4>)\n",
868 "pales (True, 4, 14, <Direction.left: 1>)\n",
869 "chairs (True, 3, 5, <Direction.downright: 8>)\n",
870 "fishy (True, 7, 5, <Direction.left: 1>)\n",
871 "capitols (True, 17, 6, <Direction.right: 2>)\n",
872 "based (True, 8, 8, <Direction.right: 2>)\n",
873 "gums (True, 6, 14, <Direction.left: 1>)\n",
874 "pheromones (True, 5, 17, <Direction.downleft: 7>)\n",
875 "saki (True, 16, 16, <Direction.up: 3>)\n",
876 "moiety (True, 11, 7, <Direction.up: 3>)\n",
877 "waxed (True, 0, 4, <Direction.left: 1>)\n",
878 "guano (True, 17, 1, <Direction.up: 3>)\n",
879 "thriven (True, 0, 6, <Direction.downleft: 7>)\n",
880 "dilate (True, 19, 19, <Direction.up: 3>)\n",
881 "moray (True, 0, 16, <Direction.down: 4>)\n",
882 "icons (True, 13, 12, <Direction.downright: 8>)\n",
883 "adman (True, 7, 13, <Direction.downleft: 7>)\n",
884 "ukulele (True, 1, 7, <Direction.right: 2>)\n",
885 "hacked (True, 3, 17, <Direction.left: 1>)\n",
886 "rope (True, 5, 8, <Direction.up: 3>)\n",
887 "rise (True, 12, 15, <Direction.upright: 6>)\n",
888 "clue (True, 4, 15, <Direction.down: 4>)\n",
889 "acted (True, 8, 19, <Direction.up: 3>)\n",
890 "nicknack (True, 19, 17, <Direction.up: 3>)\n",
891 "spar (True, 12, 4, <Direction.upleft: 5>)\n",
892 "verbs (True, 8, 1, <Direction.right: 2>)\n",
893 "boss (True, 0, 12, <Direction.right: 2>)\n",
894 "annex (True, 4, 0, <Direction.down: 4>)\n",
895 "neck (True, 14, 5, <Direction.left: 1>)\n",
896 "repeater (True, 13, 11, <Direction.upright: 6>)\n",
897 "befalls (True, 8, 8, <Direction.down: 4>)\n",
898 "drover (True, 2, 17, <Direction.left: 1>)\n",
899 "leave (True, 6, 2, <Direction.right: 2>)\n",
900 "pans (True, 1, 3, <Direction.left: 1>)\n",
901 "brave (True, 15, 2, <Direction.down: 4>)\n",
902 "brigs (True, 2, 9, <Direction.down: 4>)\n",
903 "opus (True, 10, 19, <Direction.down: 4>)\n",
904 "live (True, 1, 6, <Direction.downleft: 7>)\n",
905 "noun (True, 10, 5, <Direction.downleft: 7>)\n",
906 "tail (True, 11, 12, <Direction.downright: 8>)\n",
907 "riot (True, 2, 4, <Direction.right: 2>)\n",
908 "care (True, 14, 13, <Direction.left: 1>)\n",
909 "hits (True, 19, 13, <Direction.right: 2>)\n",
910 "quilt (True, 11, 14, <Direction.upright: 6>)\n",
911 "part (True, 3, 19, <Direction.up: 3>)\n"
917 " print(w, present(padded, w))"
922 "execution_count": 43,
926 "def decoys(grid, words, all_words, limit=100):\n",
927 " decoy_words = []\n",
928 " dlen_limit = max(len(w) for w in words)\n",
929 " while len(words) + len(decoy_words) < limit:\n",
930 " d = random.choice(all_words)\n",
931 " if d not in words and len(d) >= 4 and len(d) <= dlen_limit and not present(grid, d)[0]:\n",
932 " decoy_words += [d]\n",
933 " return decoy_words"
938 "execution_count": 44,
960 " 'overcompensating',\n",
990 "execution_count": 44,
992 "output_type": "execute_result"
996 "ds = decoys(padded, ws, ws_words)\n",
1001 "cell_type": "code",
1002 "execution_count": 45,
1009 "output_type": "stream",
1011 "opted (True, 5, 7, <Direction.downright: 8>)\n",
1012 "haywire (True, 8, 9, <Direction.downleft: 7>)\n",
1013 "busting (True, 4, 17, <Direction.left: 1>)\n",
1014 "succinct (True, 7, 17, <Direction.down: 4>)\n",
1015 "institute (True, 16, 18, <Direction.up: 3>)\n",
1016 "illicit (True, 13, 14, <Direction.left: 1>)\n",
1017 "hypersensitivity (True, 16, 15, <Direction.left: 1>)\n",
1018 "placement (True, 3, 11, <Direction.left: 1>)\n",
1019 "bathrobe (True, 0, 5, <Direction.right: 2>)\n",
1020 "inured (True, 4, 0, <Direction.right: 2>)\n",
1021 "caveats (True, 3, 8, <Direction.downleft: 7>)\n",
1022 "revisiting (True, 2, 11, <Direction.left: 1>)\n",
1023 "pulp (True, 15, 11, <Direction.right: 2>)\n",
1024 "teacup (True, 7, 16, <Direction.downleft: 7>)\n",
1025 "threading (True, 18, 6, <Direction.right: 2>)\n",
1026 "queered (True, 5, 13, <Direction.right: 2>)\n",
1027 "parking (True, 6, 9, <Direction.right: 2>)\n",
1028 "advent (True, 1, 11, <Direction.left: 1>)\n",
1029 "chasuble (True, 19, 19, <Direction.left: 1>)\n",
1030 "mosey (True, 7, 5, <Direction.downleft: 7>)\n",
1031 "highboys (True, 19, 4, <Direction.right: 2>)\n",
1032 "recharging (True, 18, 19, <Direction.up: 3>)\n",
1033 "flue (True, 12, 2, <Direction.upright: 6>)\n",
1034 "plywood (True, 3, 18, <Direction.left: 1>)\n",
1035 "gluing (True, 12, 12, <Direction.upleft: 5>)\n",
1036 "worrier (True, 1, 12, <Direction.right: 2>)\n",
1037 "karma (True, 17, 9, <Direction.left: 1>)\n",
1038 "peepers (True, 8, 7, <Direction.downleft: 7>)\n",
1039 "vulnerable (True, 17, 10, <Direction.right: 2>)\n",
1040 "boycott (True, 15, 1, <Direction.right: 2>)\n",
1041 "rummy (True, 5, 4, <Direction.left: 1>)\n",
1042 "tonic (True, 8, 13, <Direction.downleft: 7>)\n",
1043 "children (True, 15, 0, <Direction.up: 3>)\n",
1044 "reales (True, 6, 1, <Direction.right: 2>)\n",
1045 "rectal (True, 7, 15, <Direction.down: 4>)\n",
1046 "sledded (True, 14, 16, <Direction.up: 3>)\n",
1047 "collocates (True, 14, 5, <Direction.right: 2>)\n",
1048 "edict (True, 17, 4, <Direction.left: 1>)\n",
1049 "captor (True, 14, 5, <Direction.upright: 6>)\n",
1050 "amulet (True, 9, 4, <Direction.upright: 6>)\n",
1051 "slipper (True, 2, 13, <Direction.right: 2>)\n",
1052 "foot (True, 0, 0, <Direction.right: 2>)\n",
1053 "chef (True, 14, 4, <Direction.upright: 6>)\n",
1054 "goods (True, 6, 15, <Direction.right: 2>)\n",
1055 "salter (True, 1, 5, <Direction.left: 1>)\n",
1056 "crows (True, 18, 0, <Direction.right: 2>)\n",
1057 "paeans (True, 12, 14, <Direction.up: 3>)\n",
1058 "fences (True, 0, 18, <Direction.left: 1>)\n",
1059 "iron (True, 5, 9, <Direction.right: 2>)\n",
1060 "liras (True, 0, 19, <Direction.down: 4>)\n",
1061 "stages (True, 19, 16, <Direction.up: 3>)\n",
1062 "defer (True, 14, 2, <Direction.upright: 6>)\n",
1063 "racy (True, 5, 4, <Direction.downleft: 7>)\n",
1064 "gaps (True, 7, 11, <Direction.right: 2>)\n",
1065 "aspen (True, 12, 10, <Direction.upleft: 5>)\n",
1066 "rams (True, 6, 0, <Direction.downright: 8>)\n",
1067 "friendship (False, 0, 0, <Direction.left: 1>)\n",
1068 "stepping (False, 0, 0, <Direction.left: 1>)\n",
1069 "featheriest (False, 0, 0, <Direction.left: 1>)\n",
1070 "thriftily (False, 0, 0, <Direction.left: 1>)\n",
1071 "mutilation (False, 0, 0, <Direction.left: 1>)\n",
1072 "nook (False, 0, 0, <Direction.left: 1>)\n",
1073 "clewing (False, 0, 0, <Direction.left: 1>)\n",
1074 "meditated (False, 0, 0, <Direction.left: 1>)\n",
1075 "gooier (False, 0, 0, <Direction.left: 1>)\n",
1076 "cripples (False, 0, 0, <Direction.left: 1>)\n",
1077 "ponderously (False, 0, 0, <Direction.left: 1>)\n",
1078 "roundelay (False, 0, 0, <Direction.left: 1>)\n",
1079 "curtailed (False, 0, 0, <Direction.left: 1>)\n",
1080 "redeemed (False, 0, 0, <Direction.left: 1>)\n",
1081 "perimeters (False, 0, 0, <Direction.left: 1>)\n",
1082 "harelips (False, 0, 0, <Direction.left: 1>)\n",
1083 "overcompensating (False, 0, 0, <Direction.left: 1>)\n",
1084 "rejoicings (False, 0, 0, <Direction.left: 1>)\n",
1085 "adobe (False, 0, 0, <Direction.left: 1>)\n",
1086 "decreasing (False, 0, 0, <Direction.left: 1>)\n",
1087 "interstices (False, 0, 0, <Direction.left: 1>)\n",
1088 "curd (False, 0, 0, <Direction.left: 1>)\n",
1089 "orientate (False, 0, 0, <Direction.left: 1>)\n",
1090 "blueberries (False, 0, 0, <Direction.left: 1>)\n",
1091 "juniors (False, 0, 0, <Direction.left: 1>)\n",
1092 "broadloom (False, 0, 0, <Direction.left: 1>)\n",
1093 "debarring (False, 0, 0, <Direction.left: 1>)\n",
1094 "chandeliers (False, 0, 0, <Direction.left: 1>)\n",
1095 "segues (False, 0, 0, <Direction.left: 1>)\n",
1096 "army (False, 0, 0, <Direction.left: 1>)\n",
1097 "snuck (False, 0, 0, <Direction.left: 1>)\n",
1098 "pugilistic (False, 0, 0, <Direction.left: 1>)\n",
1099 "snugs (False, 0, 0, <Direction.left: 1>)\n",
1100 "dexterity (False, 0, 0, <Direction.left: 1>)\n",
1101 "dallies (False, 0, 0, <Direction.left: 1>)\n",
1102 "curving (False, 0, 0, <Direction.left: 1>)\n",
1103 "newsletter (False, 0, 0, <Direction.left: 1>)\n",
1104 "torn (False, 0, 0, <Direction.left: 1>)\n",
1105 "beaching (False, 0, 0, <Direction.left: 1>)\n",
1106 "limit (False, 0, 0, <Direction.left: 1>)\n",
1107 "blackguards (False, 0, 0, <Direction.left: 1>)\n",
1108 "breezier (False, 0, 0, <Direction.left: 1>)\n",
1109 "reoccur (False, 0, 0, <Direction.left: 1>)\n",
1110 "cabins (False, 0, 0, <Direction.left: 1>)\n"
1115 "for w in ws + ds:\n",
1116 " print(w, present(padded, w))"
1120 "cell_type": "code",
1121 "execution_count": 46,
1128 "output_type": "stream",
1130 "s.esevresed...dwhims\n",
1131 "e.tbk..vapourse.bn.d\n",
1132 "h.ificembracesnslo.e\n",
1133 "ctr.egukiwic..ddui.r\n",
1134 "inor.dwh.rips.rrftsa\n",
1135 "reue.eeisrar.tiefiyl\n",
1136 "mmli.mgrg.muarth.slc\n",
1137 "ieft.un.a.bnbuemdole\n",
1138 "nn.nesimrliertseepad\n",
1139 "ei.imeloeccdeh.epob.\n",
1140 "dfsaprlrio.saf.smri.\n",
1141 "cnpdt.ofrn.usu..ap.h\n",
1142 "oom.ispeccgntlpew.sa\n",
1143 "tcu.e.l.lu.eda.vsgin\n",
1144 "e.bdsb.oarrmneloplsg\n",
1145 "r.olaetrleromrkr.isa\n",
1146 "ibatnhd.nlpoaeic.bir\n",
1147 "eesiee.i.luepno.o.e.\n",
1148 "snt.d.o.y.pcte.p.mr.\n",
1149 "u....jtoquesylduol..\n",
1150 "sfesevresedpzcdwhims\n",
1151 "eotbkvgvapoursehbnyd\n",
1152 "hiificembracesnslone\n",
1153 "ctrnegukiwicurdduivr\n",
1154 "inorydwhrripscrrftsa\n",
1155 "reueleeisrarvtiefiyl\n",
1156 "mmlinmgrgzmuarthgslc\n",
1157 "ieftuunyanbnbuemdole\n",
1158 "nncnesimrliertseepad\n",
1159 "eirimeloeccdehzepobm\n",
1160 "dfsaprlrioisafesmriq\n",
1161 "cnpdtsofrnausuodapxh\n",
1162 "oomlispeccgntlpewasa\n",
1163 "tcuaehlzluledakvsgin\n",
1164 "eibdsbeoarrmneloplsg\n",
1165 "rbolaetrleromrkrnisa\n",
1166 "ibatnhdtnlpoaeicibir\n",
1167 "eesieerimluepnoholey\n",
1168 "sntadvoaycpctespsmro\n",
1169 "uamapjtoquesylduoldp\n",
1170 "56 words added; 8 directions\n",
1171 "Present: abreast bigwig bluff bodes bumps clothe concur confinement coteries crier cull daintier declared dendrites denim deserves embraces empties federal fluorite from glib guarded hangar herds iambic joiner kiwi loudly menus mocked panoply pearl poem polling prints proposition pruned resume rices riches rips roped rove seal seem shucks sissier swamped syllabi tine toque truthful unstabler vapours whims\n",
1172 "Decoys: abrasions adapters aimlessness alkali awakens blowing burnouses burped cattily coal commences confusion contrivance crudest curies depravity distribute diva emigrate emulsion giveaway hangman house lifeboats maze middy mines mystified obtain organic parsons postulate prefixes pretenders razors scone sloes spuds straight subtleties systematise turncoats unpacked waivers\n"
1177 "g, ws = interesting_grid()\n",
1178 "p = pad_grid(g)\n",
1179 "ds = decoys(p, ws, ws_words)\n",
1180 "print(show_grid(g))\n",
1181 "print(show_grid(p))\n",
1182 "print(len(ws), 'words added; ', len(set(present(g, w)[3] for w in ws)), 'directions')\n",
1183 "print('Present:', wcat(sorted(ws)))\n",
1184 "print('Decoys:', wcat(sorted(ds)))"
1188 "cell_type": "code",
1189 "execution_count": 63,
1196 "output_type": "stream",
1219 "14 words added; 6 directions\n",
1220 "Present: apace cowhides crazies dock knows lived mown pears probed rhubarb rioted staple tops wide\n",
1221 "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"
1226 "g, ws = interesting_grid(width=10, height=10, words_limit=5, direction_slack=6)\n",
1227 "p = pad_grid(g)\n",
1228 "ds = decoys(p, ws, ws_words)\n",
1229 "print(show_grid(g))\n",
1231 "print(show_grid(p))\n",
1232 "print(len(ws), 'words added; ', len(set(present(g, w)[3] for w in ws)), 'directions')\n",
1233 "print('Present:', wcat(sorted(ws)))\n",
1234 "print('Decoys:', wcat(sorted(ds)))"
1238 "cell_type": "code",
1239 "execution_count": 98,
1262 "execution_count": 98,
1264 "output_type": "execute_result"
1268 "ds_original = ['regards', 'perfect', 'instants', 'refined', 'coddle', 'fickler', 'gambol', 'misprint', 'tapes', 'impugns', 'moonshot', 'chump', 'brick', 'siren', 'faddish', 'winced', 'kielbasy', 'market', 'puckered', 'trains', 'welts', 'cackles', 'foaming', 'proceed', 'gliding', 'guts', 'uric', 'oaks', 'molested', 'curled', 'boor', 'solaria', 'gristle', 'bombing', 'loamier', 'ensuing', 'cunt', 'sunder', 'revel', 'coaster', 'grunts', 'mucking', 'typesets', 'carnal', 'whimsy', 'scoff', 'coccyxes', 'meanly', 'sprain', 'minuend', 'ringlet', 'fest', 'winced', 'shinier', 'dicier', 'thirds', 'olives', 'garoting', 'pastrami', 'tranquil', 'tamped', 'sunup', 'crumbled', 'throw', 'ridges', 'chaplets', 'curlier', 'lugs', 'collies', 'adapting', 'demeanor', 'deepen', 'lanyard', 'tiller', 'transfix', 'wariness', 'times', 'mitts', 'dowses', 'creels', 'curds', 'quashed', 'orgasmic', 'ibex', 'retraces', 'casino']\n",
1269 "ds = random.sample(ds_original, 15)\n",
1274 "cell_type": "code",
1275 "execution_count": 89,
1371 "execution_count": 89,
1373 "output_type": "execute_result"
1381 "cell_type": "code",
1382 "execution_count": 99,
1389 "output_type": "stream",
1391 "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",
1392 "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",
1393 "present_words = ['probed', 'staple', 'rioted', 'cowhides', 'tops', 'knows', 'lived', 'rhubarb', 'crazies', 'dock', 'apace', 'mown', 'pears', 'wide']\n",
1394 "decoy_words = ['fickler', 'adapting', 'chump', 'foaming', 'molested', 'carnal', 'crumbled', 'guts', 'minuend', 'bombing', 'winced', 'coccyxes', 'solaria', 'shinier', 'cackles']\n",
1395 "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>)`')]\n"
1400 "print('grid = ', g)\n",
1401 "print('padded_grid = ', p)\n",
1402 "print('present_words = ', ws)\n",
1403 "print('decoy_words = ', ds)\n",
1404 "print('Directions: ', [(w, '`' + str(present(g, w)) + '`') for w in ws])"
1408 "cell_type": "code",
1409 "execution_count": 100,
1415 "# with open('example-wordsearch.txt', 'w') as f:\n",
1416 "# f.write('10x10\\n')\n",
1417 "# f.write(show_grid(p))\n",
1418 "# f.write('\\n')\n",
1419 "# f.write(lcat(sorted(ws + ds)))\n",
1420 "# with open('exmaple-wordsearch-solution.txt', 'w') as f:\n",
1421 "# f.write('10x10\\n')\n",
1422 "# f.write(show_grid(g))\n",
1423 "# f.write('\\n')\n",
1424 "# f.write(lcat(sorted(ws)) + '\\n\\n')\n",
1425 "# f.write(lcat(sorted(ds)))"
1429 "cell_type": "code",
1430 "execution_count": 77,
1437 "output_type": "stream",
1439 "probed 3 0 6 Direction.down [(3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0)]\n",
1440 "staple 9 1 6 Direction.right [(9, 1), (9, 2), (9, 3), (9, 4), (9, 5), (9, 6)]\n",
1441 "rioted 6 7 6 Direction.upleft [(6, 7), (5, 6), (4, 5), (3, 4), (2, 3), (1, 2)]\n",
1442 "cowhides 8 3 8 Direction.up [(8, 3), (7, 3), (6, 3), (5, 3), (4, 3), (3, 3), (2, 3), (1, 3)]\n",
1443 "tops 7 4 4 Direction.right [(7, 4), (7, 5), (7, 6), (7, 7)]\n",
1444 "knows 8 2 5 Direction.up [(8, 2), (7, 2), (6, 2), (5, 2), (4, 2)]\n",
1445 "lived 2 4 5 Direction.downright [(2, 4), (3, 5), (4, 6), (5, 7), (6, 8)]\n",
1446 "rhubarb 2 9 7 Direction.down [(2, 9), (3, 9), (4, 9), (5, 9), (6, 9), (7, 9), (8, 9)]\n",
1447 "crazies 7 1 7 Direction.up [(7, 1), (6, 1), (5, 1), (4, 1), (3, 1), (2, 1), (1, 1)]\n",
1448 "dock 8 5 4 Direction.up [(8, 5), (7, 5), (6, 5), (5, 5)]\n",
1449 "apace 5 8 5 Direction.up [(5, 8), (4, 8), (3, 8), (2, 8), (1, 8)]\n",
1450 "mown 0 5 4 Direction.right [(0, 5), (0, 6), (0, 7), (0, 8)]\n",
1451 "pears 0 3 5 Direction.downright [(0, 3), (1, 4), (2, 5), (3, 6), (4, 7)]\n",
1452 "wide 4 4 4 Direction.upright [(4, 4), (3, 5), (2, 6), (1, 7)]\n"
1458 "[(7, 5), (2, 3), (3, 5)]"
1461 "execution_count": 77,
1463 "output_type": "execute_result"
1467 "cts = collections.Counter()\n",
1469 " _, r, c, d = present(g, w)\n",
1470 " inds = indices(g, r, c, len(w), d)\n",
1471 " for i in inds:\n",
1473 " print(w, r, c, len(w), d, inds)\n",
1474 "[i for i in cts if cts[i] > 1]"
1478 "cell_type": "code",
1479 "execution_count": null,
1487 "cell_type": "code",
1488 "execution_count": 143,
1495 "output_type": "stream",
1601 "# for i in range(100):\n",
1603 "# g, ws = interesting_grid()\n",
1604 "# p = pad_grid(g)\n",
1605 "# ds = decoys(p, ws, ws_words)\n",
1606 "# with open('wordsearch{:02}.txt'.format(i), 'w') as f:\n",
1607 "# f.write('20x20\\n')\n",
1608 "# f.write(show_grid(p))\n",
1609 "# f.write('\\n')\n",
1610 "# f.write(lcat(sorted(ws + ds)))\n",
1611 "# with open('wordsearch-solution{:02}.txt'.format(i), 'w') as f:\n",
1612 "# f.write('20x20\\n')\n",
1613 "# f.write(show_grid(g))\n",
1614 "# f.write('\\n')\n",
1615 "# f.write(lcat(sorted(ws)) + '\\n\\n')\n",
1616 "# f.write(lcat(sorted(ds)))"
1620 "cell_type": "code",
1621 "execution_count": null,
1631 "display_name": "Python 3",
1632 "language": "python",
1636 "codemirror_mode": {
1640 "file_extension": ".py",
1641 "mimetype": "text/x-python",
1643 "nbconvert_exporter": "python",
1644 "pygments_lexer": "ipython3",