More and better puzzles
[ou-summer-of-code-2017.git] / wordsearch / wordsearch-solving.ipynb
1 {
2 "cells": [
3 {
4 "cell_type": "code",
5 "execution_count": 13,
6 "metadata": {
7 "collapsed": false
8 },
9 "outputs": [],
10 "source": [
11 "import string\n",
12 "import re\n",
13 "import collections\n",
14 "import copy\n",
15 "import os\n",
16 "\n",
17 "from enum import Enum\n",
18 "Direction = Enum('Direction', 'left right up down upleft upright downleft downright')\n",
19 " \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",
24 "\n",
25 "cat = ''.join\n",
26 "wcat = ' '.join\n",
27 "lcat = '\\n'.join"
28 ]
29 },
30 {
31 "cell_type": "code",
32 "execution_count": 14,
33 "metadata": {
34 "collapsed": true
35 },
36 "outputs": [],
37 "source": [
38 "def empty_grid(w, h):\n",
39 " return [['.' for c in range(w)] for r in range(h)]"
40 ]
41 },
42 {
43 "cell_type": "code",
44 "execution_count": 15,
45 "metadata": {
46 "collapsed": true
47 },
48 "outputs": [],
49 "source": [
50 "def show_grid(grid):\n",
51 " return lcat(cat(r) for r in grid)"
52 ]
53 },
54 {
55 "cell_type": "code",
56 "execution_count": 16,
57 "metadata": {
58 "collapsed": true
59 },
60 "outputs": [],
61 "source": [
62 "def indices(grid, r, c, l, d):\n",
63 " dr, dc = delta[d]\n",
64 " w = len(grid[0])\n",
65 " h = len(grid)\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",
68 " if i >= 0\n",
69 " if j >= 0\n",
70 " if i < h\n",
71 " if j < w]"
72 ]
73 },
74 {
75 "cell_type": "code",
76 "execution_count": 17,
77 "metadata": {
78 "collapsed": true
79 },
80 "outputs": [],
81 "source": [
82 "def gslice(grid, r, c, l, d):\n",
83 " return [grid[i][j] for i, j in indices(grid, r, c, l, d)]"
84 ]
85 },
86 {
87 "cell_type": "code",
88 "execution_count": 18,
89 "metadata": {
90 "collapsed": true
91 },
92 "outputs": [],
93 "source": [
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",
96 " grid[i][j] = l\n",
97 " return grid"
98 ]
99 },
100 {
101 "cell_type": "code",
102 "execution_count": 20,
103 "metadata": {
104 "collapsed": true
105 },
106 "outputs": [],
107 "source": [
108 "def present(grid, word):\n",
109 " w = len(grid[0])\n",
110 " h = len(grid)\n",
111 " for r in range(h):\n",
112 " for c in range(w):\n",
113 " for d in Direction:\n",
114 " if cat(gslice(grid, r, c, len(word), d)) == word:\n",
115 " return True, r, c, d\n",
116 " return False, 0, 0, list(Direction)[0]"
117 ]
118 },
119 {
120 "cell_type": "code",
121 "execution_count": 22,
122 "metadata": {
123 "collapsed": false
124 },
125 "outputs": [],
126 "source": [
127 "def read_wordsearch(fn):\n",
128 " lines = [l.strip() for l in open(fn).readlines()]\n",
129 " w, h = [int(s) for s in lines[0].split('x')]\n",
130 " grid = lines[1:h+1]\n",
131 " words = lines[h+1:]\n",
132 " return w, h, grid, words"
133 ]
134 },
135 {
136 "cell_type": "code",
137 "execution_count": 50,
138 "metadata": {
139 "collapsed": false,
140 "scrolled": true
141 },
142 "outputs": [
143 {
144 "data": {
145 "text/plain": [
146 "(['pistrohxegniydutslxt',\n",
147 " 'wmregunarbpiledsyuoo',\n",
148 " 'hojminbmutartslrlmgo',\n",
149 " 'isrsdniiekildabolpll',\n",
150 " 'tstsnyekentypkalases',\n",
151 " 'ssnetengcrfetedirgdt',\n",
152 " 'religstasuslatxauner',\n",
153 " 'elgcpgatsklglzistilo',\n",
154 " 'tndlimitationilkasan',\n",
155 " 'aousropedlygiifeniog',\n",
156 " 'kilrprepszffsyzqsrhs',\n",
157 " 'itlaadorableorpccese',\n",
158 " 'noaeewoodedpngmqicnl',\n",
159 " 'gmrtoitailingchelrok',\n",
160 " 'jadsngninetsahtooeic',\n",
161 " 'xeernighestsailarmtu',\n",
162 " 'aeabsolvednscumdfnon',\n",
163 " 'gydammingawlcandornk',\n",
164 " 'hurlerslvkaccxcinosw',\n",
165 " 'iqnanoitacifitrofqqi'],\n",
166 " ['absolved',\n",
167 " 'adorable',\n",
168 " 'aeon',\n",
169 " 'alias',\n",
170 " 'ancestor',\n",
171 " 'baritone',\n",
172 " 'bemusing',\n",
173 " 'blonds',\n",
174 " 'bran',\n",
175 " 'calcite',\n",
176 " 'candor',\n",
177 " 'conciseness',\n",
178 " 'consequent',\n",
179 " 'cuddle',\n",
180 " 'damming',\n",
181 " 'dashboards',\n",
182 " 'despairing',\n",
183 " 'dint',\n",
184 " 'dullard',\n",
185 " 'dynasty',\n",
186 " 'employer',\n",
187 " 'exhorts',\n",
188 " 'feted',\n",
189 " 'fill',\n",
190 " 'flattens',\n",
191 " 'foghorn',\n",
192 " 'fortification',\n",
193 " 'freakish',\n",
194 " 'frolics',\n",
195 " 'gall',\n",
196 " 'gees',\n",
197 " 'genies',\n",
198 " 'gets',\n",
199 " 'hastening',\n",
200 " 'hits',\n",
201 " 'hopelessness',\n",
202 " 'hurlers',\n",
203 " 'impales',\n",
204 " 'infix',\n",
205 " 'inflow',\n",
206 " 'innumerable',\n",
207 " 'intentional',\n",
208 " 'jerkin',\n",
209 " 'justification',\n",
210 " 'kitty',\n",
211 " 'knuckles',\n",
212 " 'leaving',\n",
213 " 'like',\n",
214 " 'limitation',\n",
215 " 'locoweeds',\n",
216 " 'loot',\n",
217 " 'lucking',\n",
218 " 'lumps',\n",
219 " 'mercerising',\n",
220 " 'monickers',\n",
221 " 'motionless',\n",
222 " 'naturally',\n",
223 " 'nighest',\n",
224 " 'notion',\n",
225 " 'ogled',\n",
226 " 'originality',\n",
227 " 'outings',\n",
228 " 'pendulous',\n",
229 " 'piled',\n",
230 " 'pins',\n",
231 " 'pithier',\n",
232 " 'prep',\n",
233 " 'randomness',\n",
234 " 'rectors',\n",
235 " 'redrew',\n",
236 " 'reformulated',\n",
237 " 'remoteness',\n",
238 " 'retaking',\n",
239 " 'rethink',\n",
240 " 'rope',\n",
241 " 'rubier',\n",
242 " 'sailors',\n",
243 " 'scowls',\n",
244 " 'scum',\n",
245 " 'sepals',\n",
246 " 'sequencers',\n",
247 " 'serf',\n",
248 " 'shoaled',\n",
249 " 'shook',\n",
250 " 'sonic',\n",
251 " 'spottiest',\n",
252 " 'stag',\n",
253 " 'stood',\n",
254 " 'stratum',\n",
255 " 'strong',\n",
256 " 'studying',\n",
257 " 'surtaxing',\n",
258 " 'tailing',\n",
259 " 'tears',\n",
260 " 'teazles',\n",
261 " 'vans',\n",
262 " 'wardrobes',\n",
263 " 'wooded',\n",
264 " 'worsts',\n",
265 " 'zings'])"
266 ]
267 },
268 "execution_count": 50,
269 "metadata": {},
270 "output_type": "execute_result"
271 }
272 ],
273 "source": [
274 "width, height, g, ws = read_wordsearch('wordsearch04.txt')\n",
275 "g, ws"
276 ]
277 },
278 {
279 "cell_type": "code",
280 "execution_count": 51,
281 "metadata": {
282 "collapsed": false,
283 "scrolled": true
284 },
285 "outputs": [
286 {
287 "name": "stdout",
288 "output_type": "stream",
289 "text": [
290 "absolved (True, 16, 2, <Direction.right: 2>)\n",
291 "adorable (True, 11, 4, <Direction.right: 2>)\n",
292 "aeon (True, 11, 4, <Direction.down: 4>)\n",
293 "alias (True, 15, 15, <Direction.left: 1>)\n",
294 "ancestor (False, 0, 0, <Direction.left: 1>)\n",
295 "baritone (False, 0, 0, <Direction.left: 1>)\n",
296 "bemusing (False, 0, 0, <Direction.left: 1>)\n",
297 "blonds (False, 0, 0, <Direction.left: 1>)\n",
298 "bran (True, 1, 9, <Direction.left: 1>)\n",
299 "calcite (True, 19, 9, <Direction.upright: 6>)\n",
300 "candor (True, 17, 12, <Direction.right: 2>)\n",
301 "conciseness (False, 0, 0, <Direction.left: 1>)\n",
302 "consequent (False, 0, 0, <Direction.left: 1>)\n",
303 "cuddle (False, 0, 0, <Direction.left: 1>)\n",
304 "damming (True, 17, 2, <Direction.right: 2>)\n",
305 "dashboards (False, 0, 0, <Direction.left: 1>)\n",
306 "despairing (False, 0, 0, <Direction.left: 1>)\n",
307 "dint (False, 0, 0, <Direction.left: 1>)\n",
308 "dullard (True, 8, 2, <Direction.down: 4>)\n",
309 "dynasty (True, 3, 4, <Direction.downright: 8>)\n",
310 "employer (False, 0, 0, <Direction.left: 1>)\n",
311 "exhorts (True, 0, 8, <Direction.left: 1>)\n",
312 "feted (True, 5, 10, <Direction.right: 2>)\n",
313 "fill (True, 9, 14, <Direction.upleft: 5>)\n",
314 "flattens (True, 10, 10, <Direction.upleft: 5>)\n",
315 "foghorn (True, 10, 11, <Direction.downright: 8>)\n",
316 "fortification (True, 19, 16, <Direction.left: 1>)\n",
317 "freakish (False, 0, 0, <Direction.left: 1>)\n",
318 "frolics (True, 16, 16, <Direction.up: 3>)\n",
319 "gall (False, 0, 0, <Direction.left: 1>)\n",
320 "gees (True, 17, 0, <Direction.upright: 6>)\n",
321 "genies (True, 5, 7, <Direction.upleft: 5>)\n",
322 "gets (True, 6, 4, <Direction.upleft: 5>)\n",
323 "hastening (True, 14, 13, <Direction.left: 1>)\n",
324 "hits (True, 2, 0, <Direction.down: 4>)\n",
325 "hopelessness (False, 0, 0, <Direction.left: 1>)\n",
326 "hurlers (True, 18, 0, <Direction.right: 2>)\n",
327 "impales (False, 0, 0, <Direction.left: 1>)\n",
328 "infix (False, 0, 0, <Direction.left: 1>)\n",
329 "inflow (False, 0, 0, <Direction.left: 1>)\n",
330 "innumerable (False, 0, 0, <Direction.left: 1>)\n",
331 "intentional (False, 0, 0, <Direction.left: 1>)\n",
332 "jerkin (False, 0, 0, <Direction.left: 1>)\n",
333 "justification (False, 0, 0, <Direction.left: 1>)\n",
334 "kitty (True, 8, 15, <Direction.upleft: 5>)\n",
335 "knuckles (True, 17, 19, <Direction.up: 3>)\n",
336 "leaving (False, 0, 0, <Direction.left: 1>)\n",
337 "like (True, 3, 11, <Direction.left: 1>)\n",
338 "limitation (True, 8, 3, <Direction.right: 2>)\n",
339 "locoweeds (False, 0, 0, <Direction.left: 1>)\n",
340 "loot (True, 3, 19, <Direction.up: 3>)\n",
341 "lucking (True, 7, 10, <Direction.upleft: 5>)\n",
342 "lumps (True, 0, 17, <Direction.down: 4>)\n",
343 "mercerising (True, 15, 17, <Direction.up: 3>)\n",
344 "monickers (False, 0, 0, <Direction.left: 1>)\n",
345 "motionless (True, 13, 1, <Direction.up: 3>)\n",
346 "naturally (True, 9, 16, <Direction.up: 3>)\n",
347 "nighest (True, 15, 4, <Direction.right: 2>)\n",
348 "notion (True, 17, 18, <Direction.up: 3>)\n",
349 "ogled (True, 1, 18, <Direction.down: 4>)\n",
350 "originality (False, 0, 0, <Direction.left: 1>)\n",
351 "outings (False, 0, 0, <Direction.left: 1>)\n",
352 "pendulous (False, 0, 0, <Direction.left: 1>)\n",
353 "piled (True, 1, 10, <Direction.right: 2>)\n",
354 "pins (True, 7, 4, <Direction.upleft: 5>)\n",
355 "pithier (False, 0, 0, <Direction.left: 1>)\n",
356 "prep (True, 10, 4, <Direction.right: 2>)\n",
357 "randomness (False, 0, 0, <Direction.left: 1>)\n",
358 "rectors (False, 0, 0, <Direction.left: 1>)\n",
359 "redrew (False, 0, 0, <Direction.left: 1>)\n",
360 "reformulated (False, 0, 0, <Direction.left: 1>)\n",
361 "remoteness (False, 0, 0, <Direction.left: 1>)\n",
362 "retaking (True, 6, 0, <Direction.down: 4>)\n",
363 "rethink (False, 0, 0, <Direction.left: 1>)\n",
364 "rope (True, 9, 4, <Direction.right: 2>)\n",
365 "rubier (True, 0, 4, <Direction.downright: 8>)\n",
366 "sailors (True, 7, 15, <Direction.up: 3>)\n",
367 "scowls (False, 0, 0, <Direction.left: 1>)\n",
368 "scum (True, 16, 11, <Direction.right: 2>)\n",
369 "sepals (True, 6, 10, <Direction.upright: 6>)\n",
370 "sequencers (False, 0, 0, <Direction.left: 1>)\n",
371 "serf (False, 0, 0, <Direction.left: 1>)\n",
372 "shoaled (True, 11, 18, <Direction.up: 3>)\n",
373 "shook (False, 0, 0, <Direction.left: 1>)\n",
374 "sonic (True, 18, 18, <Direction.left: 1>)\n",
375 "spottiest (False, 0, 0, <Direction.left: 1>)\n",
376 "stag (True, 7, 8, <Direction.left: 1>)\n",
377 "stood (False, 0, 0, <Direction.left: 1>)\n",
378 "stratum (True, 2, 13, <Direction.left: 1>)\n",
379 "strong (True, 4, 19, <Direction.down: 4>)\n",
380 "studying (True, 0, 16, <Direction.left: 1>)\n",
381 "surtaxing (False, 0, 0, <Direction.left: 1>)\n",
382 "tailing (True, 13, 6, <Direction.right: 2>)\n",
383 "tears (True, 13, 3, <Direction.up: 3>)\n",
384 "teazles (True, 4, 10, <Direction.downright: 8>)\n",
385 "vans (True, 18, 8, <Direction.upright: 6>)\n",
386 "wardrobes (False, 0, 0, <Direction.left: 1>)\n",
387 "wooded (True, 12, 5, <Direction.right: 2>)\n",
388 "worsts (True, 1, 0, <Direction.downright: 8>)\n",
389 "zings (True, 10, 14, <Direction.upleft: 5>)\n"
390 ]
391 }
392 ],
393 "source": [
394 "for w in ws:\n",
395 " print(w, present(g, w))"
396 ]
397 },
398 {
399 "cell_type": "markdown",
400 "metadata": {},
401 "source": [
402 "Which words are present?"
403 ]
404 },
405 {
406 "cell_type": "code",
407 "execution_count": 52,
408 "metadata": {
409 "collapsed": false,
410 "scrolled": true
411 },
412 "outputs": [
413 {
414 "data": {
415 "text/plain": [
416 "['absolved',\n",
417 " 'adorable',\n",
418 " 'aeon',\n",
419 " 'alias',\n",
420 " 'bran',\n",
421 " 'calcite',\n",
422 " 'candor',\n",
423 " 'damming',\n",
424 " 'dullard',\n",
425 " 'dynasty',\n",
426 " 'exhorts',\n",
427 " 'feted',\n",
428 " 'fill',\n",
429 " 'flattens',\n",
430 " 'foghorn',\n",
431 " 'fortification',\n",
432 " 'frolics',\n",
433 " 'gees',\n",
434 " 'genies',\n",
435 " 'gets',\n",
436 " 'hastening',\n",
437 " 'hits',\n",
438 " 'hurlers',\n",
439 " 'kitty',\n",
440 " 'knuckles',\n",
441 " 'like',\n",
442 " 'limitation',\n",
443 " 'loot',\n",
444 " 'lucking',\n",
445 " 'lumps',\n",
446 " 'mercerising',\n",
447 " 'motionless',\n",
448 " 'naturally',\n",
449 " 'nighest',\n",
450 " 'notion',\n",
451 " 'ogled',\n",
452 " 'piled',\n",
453 " 'pins',\n",
454 " 'prep',\n",
455 " 'retaking',\n",
456 " 'rope',\n",
457 " 'rubier',\n",
458 " 'sailors',\n",
459 " 'scum',\n",
460 " 'sepals',\n",
461 " 'shoaled',\n",
462 " 'sonic',\n",
463 " 'stag',\n",
464 " 'stratum',\n",
465 " 'strong',\n",
466 " 'studying',\n",
467 " 'tailing',\n",
468 " 'tears',\n",
469 " 'teazles',\n",
470 " 'vans',\n",
471 " 'wooded',\n",
472 " 'worsts',\n",
473 " 'zings']"
474 ]
475 },
476 "execution_count": 52,
477 "metadata": {},
478 "output_type": "execute_result"
479 }
480 ],
481 "source": [
482 "[w for w in ws if present(g, w)[0]]"
483 ]
484 },
485 {
486 "cell_type": "markdown",
487 "metadata": {},
488 "source": [
489 "What is the longest word that is present?"
490 ]
491 },
492 {
493 "cell_type": "code",
494 "execution_count": 53,
495 "metadata": {
496 "collapsed": false
497 },
498 "outputs": [
499 {
500 "data": {
501 "text/plain": [
502 "'fortification'"
503 ]
504 },
505 "execution_count": 53,
506 "metadata": {},
507 "output_type": "execute_result"
508 }
509 ],
510 "source": [
511 "sorted([w for w in ws if present(g, w)[0]], key=len)[-1]"
512 ]
513 },
514 {
515 "cell_type": "markdown",
516 "metadata": {},
517 "source": [
518 "What is the longest word that is absent?"
519 ]
520 },
521 {
522 "cell_type": "code",
523 "execution_count": 54,
524 "metadata": {
525 "collapsed": false
526 },
527 "outputs": [
528 {
529 "data": {
530 "text/plain": [
531 "'justification'"
532 ]
533 },
534 "execution_count": 54,
535 "metadata": {},
536 "output_type": "execute_result"
537 }
538 ],
539 "source": [
540 "sorted([w for w in ws if not present(g, w)[0]], key=len)[-1]"
541 ]
542 },
543 {
544 "cell_type": "markdown",
545 "metadata": {},
546 "source": [
547 "How many letters are unused?"
548 ]
549 },
550 {
551 "cell_type": "code",
552 "execution_count": 55,
553 "metadata": {
554 "collapsed": false
555 },
556 "outputs": [
557 {
558 "data": {
559 "text/plain": [
560 "57"
561 ]
562 },
563 "execution_count": 55,
564 "metadata": {},
565 "output_type": "execute_result"
566 }
567 ],
568 "source": [
569 "g0 = empty_grid(width, height)\n",
570 "for w in ws:\n",
571 " p, r, c, d = present(g, w)\n",
572 " if p:\n",
573 " set_grid(g0, r, c, d, w)\n",
574 "len([c for c in cat(cat(l) for l in g0) if c == '.'])"
575 ]
576 },
577 {
578 "cell_type": "markdown",
579 "metadata": {},
580 "source": [
581 "What is the longest word you can make form the leftover letters?"
582 ]
583 },
584 {
585 "cell_type": "code",
586 "execution_count": 56,
587 "metadata": {
588 "collapsed": false,
589 "scrolled": true
590 },
591 "outputs": [
592 {
593 "data": {
594 "text/plain": [
595 "Counter({'a': 4,\n",
596 " 'b': 1,\n",
597 " 'c': 5,\n",
598 " 'd': 3,\n",
599 " 'e': 1,\n",
600 " 'g': 2,\n",
601 " 'i': 5,\n",
602 " 'j': 2,\n",
603 " 'k': 3,\n",
604 " 'l': 2,\n",
605 " 'm': 3,\n",
606 " 'n': 3,\n",
607 " 'p': 3,\n",
608 " 'q': 5,\n",
609 " 'r': 3,\n",
610 " 's': 3,\n",
611 " 'w': 2,\n",
612 " 'x': 4,\n",
613 " 'y': 2,\n",
614 " 'z': 1})"
615 ]
616 },
617 "execution_count": 56,
618 "metadata": {},
619 "output_type": "execute_result"
620 }
621 ],
622 "source": [
623 "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",
624 " if u == '.']\n",
625 "unused_letter_count = collections.Counter(unused_letters)\n",
626 "unused_letter_count"
627 ]
628 },
629 {
630 "cell_type": "code",
631 "execution_count": 57,
632 "metadata": {
633 "collapsed": false,
634 "scrolled": true
635 },
636 "outputs": [
637 {
638 "data": {
639 "text/plain": [
640 "['ancestor',\n",
641 " 'baritone',\n",
642 " 'bemusing',\n",
643 " 'blonds',\n",
644 " 'conciseness',\n",
645 " 'consequent',\n",
646 " 'cuddle',\n",
647 " 'dashboards',\n",
648 " 'despairing',\n",
649 " 'dint',\n",
650 " 'employer',\n",
651 " 'freakish',\n",
652 " 'gall',\n",
653 " 'hopelessness',\n",
654 " 'impales',\n",
655 " 'infix',\n",
656 " 'inflow',\n",
657 " 'innumerable',\n",
658 " 'intentional',\n",
659 " 'jerkin',\n",
660 " 'justification',\n",
661 " 'leaving',\n",
662 " 'locoweeds',\n",
663 " 'monickers',\n",
664 " 'originality',\n",
665 " 'outings',\n",
666 " 'pendulous',\n",
667 " 'pithier',\n",
668 " 'randomness',\n",
669 " 'rectors',\n",
670 " 'redrew',\n",
671 " 'reformulated',\n",
672 " 'remoteness',\n",
673 " 'rethink',\n",
674 " 'scowls',\n",
675 " 'sequencers',\n",
676 " 'serf',\n",
677 " 'shook',\n",
678 " 'spottiest',\n",
679 " 'stood',\n",
680 " 'surtaxing',\n",
681 " 'wardrobes']"
682 ]
683 },
684 "execution_count": 57,
685 "metadata": {},
686 "output_type": "execute_result"
687 }
688 ],
689 "source": [
690 "unused_words = [w for w in ws if not present(g, w)[0]]\n",
691 "unused_words"
692 ]
693 },
694 {
695 "cell_type": "code",
696 "execution_count": 59,
697 "metadata": {
698 "collapsed": false,
699 "scrolled": true
700 },
701 "outputs": [
702 {
703 "name": "stdout",
704 "output_type": "stream",
705 "text": [
706 "ancestor Counter({'c': 1, 'a': 1, 's': 1, 't': 1, 'n': 1, 'r': 1, 'o': 1, 'e': 1})\n",
707 "baritone Counter({'a': 1, 'i': 1, 'r': 1, 't': 1, 'b': 1, 'n': 1, 'o': 1, 'e': 1})\n",
708 "bemusing Counter({'g': 1, 'u': 1, 'i': 1, 's': 1, 'n': 1, 'm': 1, 'b': 1, 'e': 1})\n",
709 "blonds Counter({'s': 1, 'd': 1, 'n': 1, 'b': 1, 'o': 1, 'l': 1})\n",
710 "conciseness Counter({'s': 3, 'c': 2, 'n': 2, 'e': 2, 'i': 1, 'o': 1})\n",
711 "consequent Counter({'n': 2, 'e': 2, 'u': 1, 'c': 1, 's': 1, 't': 1, 'q': 1, 'o': 1})\n",
712 "cuddle Counter({'d': 2, 'u': 1, 'e': 1, 'c': 1, 'l': 1})\n",
713 "dashboards Counter({'a': 2, 's': 2, 'd': 2, 'o': 1, 'r': 1, 'b': 1, 'h': 1})\n",
714 "*despairing Counter({'i': 2, 'g': 1, 'a': 1, 's': 1, 'r': 1, 'd': 1, 'n': 1, 'p': 1, 'e': 1})\n",
715 "dint Counter({'d': 1, 'n': 1, 'i': 1, 't': 1})\n",
716 "employer Counter({'e': 2, 'y': 1, 'r': 1, 'm': 1, 'p': 1, 'o': 1, 'l': 1})\n",
717 "freakish Counter({'k': 1, 'a': 1, 'i': 1, 'r': 1, 'f': 1, 's': 1, 'h': 1, 'e': 1})\n",
718 "*gall Counter({'l': 2, 'g': 1, 'a': 1})\n",
719 "hopelessness Counter({'s': 4, 'e': 3, 'h': 1, 'n': 1, 'p': 1, 'o': 1, 'l': 1})\n",
720 "*impales Counter({'s': 1, 'a': 1, 'i': 1, 'm': 1, 'e': 1, 'p': 1, 'l': 1})\n",
721 "infix Counter({'i': 2, 'f': 1, 'n': 1, 'x': 1})\n",
722 "inflow Counter({'i': 1, 'w': 1, 'f': 1, 'n': 1, 'o': 1, 'l': 1})\n",
723 "innumerable Counter({'n': 2, 'e': 2, 'u': 1, 'l': 1, 'a': 1, 'i': 1, 'r': 1, 'm': 1, 'b': 1})\n",
724 "intentional Counter({'n': 3, 'i': 2, 't': 2, 'a': 1, 'l': 1, 'o': 1, 'e': 1})\n",
725 "*jerkin Counter({'k': 1, 'i': 1, 'r': 1, 'n': 1, 'j': 1, 'e': 1})\n",
726 "justification Counter({'i': 3, 't': 2, 'u': 1, 'c': 1, 's': 1, 'n': 1, 'f': 1, 'j': 1, 'o': 1, 'a': 1})\n",
727 "leaving Counter({'g': 1, 'l': 1, 'a': 1, 'i': 1, 'n': 1, 'v': 1, 'e': 1})\n",
728 "locoweeds Counter({'o': 2, 'e': 2, 'c': 1, 's': 1, 'd': 1, 'w': 1, 'l': 1})\n",
729 "monickers Counter({'k': 1, 'c': 1, 's': 1, 'i': 1, 'r': 1, 'n': 1, 'm': 1, 'o': 1, 'e': 1})\n",
730 "originality Counter({'i': 3, 'g': 1, 'a': 1, 'r': 1, 't': 1, 'n': 1, 'y': 1, 'o': 1, 'l': 1})\n",
731 "outings Counter({'g': 1, 'u': 1, 's': 1, 'i': 1, 't': 1, 'n': 1, 'o': 1})\n",
732 "pendulous Counter({'u': 2, 's': 1, 'd': 1, 'n': 1, 'l': 1, 'p': 1, 'o': 1, 'e': 1})\n",
733 "pithier Counter({'i': 2, 'r': 1, 't': 1, 'p': 1, 'h': 1, 'e': 1})\n",
734 "randomness Counter({'s': 2, 'n': 2, 'a': 1, 'r': 1, 'd': 1, 'm': 1, 'o': 1, 'e': 1})\n",
735 "rectors Counter({'r': 2, 'c': 1, 's': 1, 't': 1, 'o': 1, 'e': 1})\n",
736 "redrew Counter({'e': 2, 'r': 2, 'd': 1, 'w': 1})\n",
737 "reformulated Counter({'r': 2, 'e': 2, 'u': 1, 'a': 1, 'f': 1, 't': 1, 'm': 1, 'l': 1, 'd': 1, 'o': 1})\n",
738 "remoteness Counter({'e': 3, 's': 2, 'r': 1, 't': 1, 'm': 1, 'n': 1, 'o': 1})\n",
739 "rethink Counter({'k': 1, 'i': 1, 'r': 1, 't': 1, 'n': 1, 'h': 1, 'e': 1})\n",
740 "scowls Counter({'s': 2, 'w': 1, 'c': 1, 'o': 1, 'l': 1})\n",
741 "sequencers Counter({'e': 3, 's': 2, 'u': 1, 'c': 1, 'r': 1, 'n': 1, 'q': 1})\n",
742 "serf Counter({'f': 1, 'r': 1, 's': 1, 'e': 1})\n",
743 "shook Counter({'o': 2, 'k': 1, 'h': 1, 's': 1})\n",
744 "spottiest Counter({'t': 3, 's': 2, 'i': 1, 'p': 1, 'o': 1, 'e': 1})\n",
745 "stood Counter({'o': 2, 'd': 1, 't': 1, 's': 1})\n",
746 "surtaxing Counter({'i': 1, 'u': 1, 'x': 1, 'g': 1, 'a': 1, 's': 1, 'r': 1, 't': 1, 'n': 1})\n",
747 "wardrobes Counter({'r': 2, 'a': 1, 's': 1, 'd': 1, 'w': 1, 'b': 1, 'o': 1, 'e': 1})\n"
748 ]
749 }
750 ],
751 "source": [
752 "makeable_words = []\n",
753 "for w in unused_words:\n",
754 " unused_word_count = collections.Counter(w)\n",
755 " if all(unused_word_count[l] <= unused_letter_count[l] for l in unused_word_count):\n",
756 " makeable_words += [w]\n",
757 " print('*', end='')\n",
758 " print(w, unused_word_count)"
759 ]
760 },
761 {
762 "cell_type": "code",
763 "execution_count": 48,
764 "metadata": {
765 "collapsed": false
766 },
767 "outputs": [
768 {
769 "data": {
770 "text/plain": [
771 "10"
772 ]
773 },
774 "execution_count": 48,
775 "metadata": {},
776 "output_type": "execute_result"
777 }
778 ],
779 "source": [
780 "max(len(w) for w in makeable_words)"
781 ]
782 },
783 {
784 "cell_type": "code",
785 "execution_count": 49,
786 "metadata": {
787 "collapsed": false
788 },
789 "outputs": [
790 {
791 "data": {
792 "text/plain": [
793 "'despairing'"
794 ]
795 },
796 "execution_count": 49,
797 "metadata": {},
798 "output_type": "execute_result"
799 }
800 ],
801 "source": [
802 "sorted(makeable_words, key=len)[-1]"
803 ]
804 },
805 {
806 "cell_type": "code",
807 "execution_count": 74,
808 "metadata": {
809 "collapsed": false
810 },
811 "outputs": [],
812 "source": [
813 "def do_wordsearch_tasks(fn, show_anyway=False):\n",
814 " width, height, grid, words = read_wordsearch(fn)\n",
815 " used_words = [w for w in words if present(grid, w)[0]]\n",
816 " unused_words = [w for w in words if not present(grid, w)[0]]\n",
817 " lwp = sorted([w for w in words if present(grid, w)[0]], key=len)[-1]\n",
818 " lwps = [w for w in used_words if len(w) == len(lwp)]\n",
819 " lwa = sorted(unused_words, key=len)[-1]\n",
820 " lwas = [w for w in unused_words if len(w) == len(lwa)]\n",
821 " g0 = empty_grid(width, height)\n",
822 " for w in words:\n",
823 " p, r, c, d = present(grid, w)\n",
824 " if p:\n",
825 " set_grid(g0, r, c, d, w) \n",
826 " 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",
827 " if u == '.']\n",
828 " unused_letter_count = collections.Counter(unused_letters)\n",
829 " makeable_words = []\n",
830 " for w in unused_words:\n",
831 " unused_word_count = collections.Counter(w)\n",
832 " if all(unused_word_count[l] <= unused_letter_count[l] for l in unused_word_count):\n",
833 " makeable_words += [w]\n",
834 " lwm = sorted(makeable_words, key=len)[-1]\n",
835 " lwms = [w for w in makeable_words if len(w) == len(lwm)]\n",
836 " if show_anyway or len(set((len(lwp),len(lwa),len(lwm)))) == 3:\n",
837 " print('\\n{}'.format(fn))\n",
838 " print('{} words present'.format(len(words) - len(unused_words)))\n",
839 " print('Longest word present: {}, {} letters ({})'.format(lwp, len(lwp), lwps))\n",
840 " print('Longest word absent: {}, {} letters ({})'.format(lwa, len(lwa), lwas))\n",
841 " print('{} unused letters'.format(len([c for c in cat(cat(l) for l in g0) if c == '.'])))\n",
842 " print('Longest makeable word: {}, {} ({})'.format(lwm, len(lwm), lwms))"
843 ]
844 },
845 {
846 "cell_type": "code",
847 "execution_count": 75,
848 "metadata": {
849 "collapsed": false
850 },
851 "outputs": [
852 {
853 "name": "stdout",
854 "output_type": "stream",
855 "text": [
856 "\n",
857 "wordsearch04.txt\n",
858 "58 words present\n",
859 "Longest word present: fortification, 13 letters (['fortification'])\n",
860 "Longest word absent: justification, 13 letters (['justification'])\n",
861 "57 unused letters\n",
862 "Longest makeable word: despairing, 10 (['despairing'])\n"
863 ]
864 }
865 ],
866 "source": [
867 "do_wordsearch_tasks('wordsearch04.txt', show_anyway=True)"
868 ]
869 },
870 {
871 "cell_type": "code",
872 "execution_count": 70,
873 "metadata": {
874 "collapsed": false
875 },
876 "outputs": [
877 {
878 "name": "stdout",
879 "output_type": "stream",
880 "text": [
881 "\n",
882 "wordsearch08.txt\n",
883 "62 words present\n",
884 "Longest word present: compassionately, 15 letters (['compassionately'])\n",
885 "Longest word absent: retrospectives, 14 letters (['retrospectives'])\n",
886 "65 unused letters\n",
887 "Longest makeable word: vacationing, 11 (['vacationing'])\n"
888 ]
889 }
890 ],
891 "source": [
892 "do_wordsearch_tasks('wordsearch08.txt')"
893 ]
894 },
895 {
896 "cell_type": "code",
897 "execution_count": 76,
898 "metadata": {
899 "collapsed": false,
900 "scrolled": true
901 },
902 "outputs": [
903 {
904 "name": "stdout",
905 "output_type": "stream",
906 "text": [
907 "\n",
908 "wordsearch08.txt\n",
909 "62 words present\n",
910 "Longest word present: compassionately, 15 letters (['compassionately'])\n",
911 "Longest word absent: retrospectives, 14 letters (['retrospectives'])\n",
912 "65 unused letters\n",
913 "Longest makeable word: vacationing, 11 (['vacationing'])\n",
914 "\n",
915 "wordsearch17.txt\n",
916 "58 words present\n",
917 "Longest word present: complementing, 13 letters (['complementing'])\n",
918 "Longest word absent: upholstering, 12 letters (['domestically', 'upholstering'])\n",
919 "56 unused letters\n",
920 "Longest makeable word: plunderer, 9 (['plunderer'])\n",
921 "\n",
922 "wordsearch32.txt\n",
923 "60 words present\n",
924 "Longest word present: reciprocating, 13 letters (['reciprocating'])\n",
925 "Longest word absent: parenthesise, 12 letters (['collectibles', 'frontrunners', 'parenthesise'])\n",
926 "65 unused letters\n",
927 "Longest makeable word: sultanas, 8 (['sultanas'])\n",
928 "\n",
929 "wordsearch52.txt\n",
930 "51 words present\n",
931 "Longest word present: prefabricated, 13 letters (['prefabricated'])\n",
932 "Longest word absent: catastrophic, 12 letters (['capitalistic', 'catastrophic'])\n",
933 "86 unused letters\n",
934 "Longest makeable word: unimpressed, 11 (['bloodstream', 'brainstorms', 'reassembles', 'rhapsodises', 'synergistic', 'unimpressed'])\n",
935 "\n",
936 "wordsearch62.txt\n",
937 "58 words present\n",
938 "Longest word present: diametrically, 13 letters (['diametrically'])\n",
939 "Longest word absent: streetlights, 12 letters (['harmonically', 'skyrocketing', 'streetlights'])\n",
940 "59 unused letters\n",
941 "Longest makeable word: tabernacle, 10 (['falterings', 'tabernacle'])\n",
942 "\n",
943 "wordsearch76.txt\n",
944 "60 words present\n",
945 "Longest word present: bloodthirstier, 14 letters (['bloodthirstier'])\n",
946 "Longest word absent: incriminating, 13 letters (['incriminating'])\n",
947 "59 unused letters\n",
948 "Longest makeable word: stubbornly, 10 (['leafletted', 'stubbornly'])\n",
949 "\n",
950 "wordsearch94.txt\n",
951 "59 words present\n",
952 "Longest word present: unforgettable, 13 letters (['unforgettable'])\n",
953 "Longest word absent: accommodated, 12 letters (['accommodated'])\n",
954 "69 unused letters\n",
955 "Longest makeable word: respectably, 11 (['predictions', 'respectably'])\n"
956 ]
957 }
958 ],
959 "source": [
960 "for fn in sorted(os.listdir()):\n",
961 " if re.match('wordsearch\\d\\d\\.txt', fn):\n",
962 " do_wordsearch_tasks(fn)"
963 ]
964 },
965 {
966 "cell_type": "code",
967 "execution_count": 38,
968 "metadata": {
969 "collapsed": false
970 },
971 "outputs": [
972 {
973 "name": "stdout",
974 "output_type": "stream",
975 "text": [
976 "absolved (True, 16, 2, <Direction.right: 2>)\n",
977 "adorable (True, 11, 4, <Direction.right: 2>)\n",
978 "aeon (True, 11, 4, <Direction.down: 4>)\n",
979 "alias (True, 15, 15, <Direction.left: 1>)\n",
980 "ancestor (False, 0, 0, <Direction.left: 1>)\n",
981 "baritone (False, 0, 0, <Direction.left: 1>)\n",
982 "bemusing (False, 0, 0, <Direction.left: 1>)\n",
983 "blonds (False, 0, 0, <Direction.left: 1>)\n",
984 "bran (True, 1, 9, <Direction.left: 1>)\n",
985 "calcite (True, 19, 9, <Direction.upright: 6>)\n",
986 "candor (True, 17, 12, <Direction.right: 2>)\n",
987 "conciseness (False, 0, 0, <Direction.left: 1>)\n",
988 "consequent (False, 0, 0, <Direction.left: 1>)\n",
989 "cuddle (False, 0, 0, <Direction.left: 1>)\n",
990 "damming (True, 17, 2, <Direction.right: 2>)\n",
991 "dashboards (False, 0, 0, <Direction.left: 1>)\n",
992 "despairing (False, 0, 0, <Direction.left: 1>)\n",
993 "dint (False, 0, 0, <Direction.left: 1>)\n",
994 "dullard (True, 8, 2, <Direction.down: 4>)\n",
995 "dynasty (True, 3, 4, <Direction.downright: 8>)\n",
996 "employer (False, 0, 0, <Direction.left: 1>)\n",
997 "exhorts (True, 0, 8, <Direction.left: 1>)\n",
998 "feted (True, 5, 10, <Direction.right: 2>)\n",
999 "fill (True, 9, 14, <Direction.upleft: 5>)\n",
1000 "flattens (True, 10, 10, <Direction.upleft: 5>)\n",
1001 "foghorn (True, 10, 11, <Direction.downright: 8>)\n",
1002 "fortification (True, 19, 16, <Direction.left: 1>)\n",
1003 "freakish (False, 0, 0, <Direction.left: 1>)\n",
1004 "frolics (True, 16, 16, <Direction.up: 3>)\n",
1005 "gall (False, 0, 0, <Direction.left: 1>)\n",
1006 "gees (True, 17, 0, <Direction.upright: 6>)\n",
1007 "genies (True, 5, 7, <Direction.upleft: 5>)\n",
1008 "gets (True, 6, 4, <Direction.upleft: 5>)\n",
1009 "hastening (True, 14, 13, <Direction.left: 1>)\n",
1010 "hits (True, 2, 0, <Direction.down: 4>)\n",
1011 "hopelessness (False, 0, 0, <Direction.left: 1>)\n",
1012 "hurlers (True, 18, 0, <Direction.right: 2>)\n",
1013 "impales (False, 0, 0, <Direction.left: 1>)\n",
1014 "infix (False, 0, 0, <Direction.left: 1>)\n",
1015 "inflow (False, 0, 0, <Direction.left: 1>)\n",
1016 "innumerable (False, 0, 0, <Direction.left: 1>)\n",
1017 "intentional (False, 0, 0, <Direction.left: 1>)\n",
1018 "jerkin (False, 0, 0, <Direction.left: 1>)\n",
1019 "justification (False, 0, 0, <Direction.left: 1>)\n",
1020 "kitty (True, 8, 15, <Direction.upleft: 5>)\n",
1021 "knuckles (True, 17, 19, <Direction.up: 3>)\n",
1022 "leaving (False, 0, 0, <Direction.left: 1>)\n",
1023 "like (True, 3, 11, <Direction.left: 1>)\n",
1024 "limitation (True, 8, 3, <Direction.right: 2>)\n",
1025 "locoweeds (False, 0, 0, <Direction.left: 1>)\n",
1026 "loot (True, 3, 19, <Direction.up: 3>)\n",
1027 "lucking (True, 7, 10, <Direction.upleft: 5>)\n",
1028 "lumps (True, 0, 17, <Direction.down: 4>)\n",
1029 "mercerising (True, 15, 17, <Direction.up: 3>)\n",
1030 "monickers (False, 0, 0, <Direction.left: 1>)\n",
1031 "motionless (True, 13, 1, <Direction.up: 3>)\n",
1032 "naturally (True, 9, 16, <Direction.up: 3>)\n",
1033 "nighest (True, 15, 4, <Direction.right: 2>)\n",
1034 "notion (True, 17, 18, <Direction.up: 3>)\n",
1035 "ogled (True, 1, 18, <Direction.down: 4>)\n",
1036 "originality (False, 0, 0, <Direction.left: 1>)\n",
1037 "outings (False, 0, 0, <Direction.left: 1>)\n",
1038 "pendulous (False, 0, 0, <Direction.left: 1>)\n",
1039 "piled (True, 1, 10, <Direction.right: 2>)\n",
1040 "pins (True, 7, 4, <Direction.upleft: 5>)\n",
1041 "pithier (False, 0, 0, <Direction.left: 1>)\n",
1042 "prep (True, 10, 4, <Direction.right: 2>)\n",
1043 "randomness (False, 0, 0, <Direction.left: 1>)\n",
1044 "rectors (False, 0, 0, <Direction.left: 1>)\n",
1045 "redrew (False, 0, 0, <Direction.left: 1>)\n",
1046 "reformulated (False, 0, 0, <Direction.left: 1>)\n",
1047 "remoteness (False, 0, 0, <Direction.left: 1>)\n",
1048 "retaking (True, 6, 0, <Direction.down: 4>)\n",
1049 "rethink (False, 0, 0, <Direction.left: 1>)\n",
1050 "rope (True, 9, 4, <Direction.right: 2>)\n",
1051 "rubier (True, 0, 4, <Direction.downright: 8>)\n",
1052 "sailors (True, 7, 15, <Direction.up: 3>)\n",
1053 "scowls (False, 0, 0, <Direction.left: 1>)\n",
1054 "scum (True, 16, 11, <Direction.right: 2>)\n",
1055 "sepals (True, 6, 10, <Direction.upright: 6>)\n",
1056 "sequencers (False, 0, 0, <Direction.left: 1>)\n",
1057 "serf (False, 0, 0, <Direction.left: 1>)\n",
1058 "shoaled (True, 11, 18, <Direction.up: 3>)\n",
1059 "shook (False, 0, 0, <Direction.left: 1>)\n",
1060 "sonic (True, 18, 18, <Direction.left: 1>)\n",
1061 "spottiest (False, 0, 0, <Direction.left: 1>)\n",
1062 "stag (True, 7, 8, <Direction.left: 1>)\n",
1063 "stood (False, 0, 0, <Direction.left: 1>)\n",
1064 "stratum (True, 2, 13, <Direction.left: 1>)\n",
1065 "strong (True, 4, 19, <Direction.down: 4>)\n",
1066 "studying (True, 0, 16, <Direction.left: 1>)\n",
1067 "surtaxing (False, 0, 0, <Direction.left: 1>)\n",
1068 "tailing (True, 13, 6, <Direction.right: 2>)\n",
1069 "tears (True, 13, 3, <Direction.up: 3>)\n",
1070 "teazles (True, 4, 10, <Direction.downright: 8>)\n",
1071 "vans (True, 18, 8, <Direction.upright: 6>)\n",
1072 "wardrobes (False, 0, 0, <Direction.left: 1>)\n",
1073 "wooded (True, 12, 5, <Direction.right: 2>)\n",
1074 "worsts (True, 1, 0, <Direction.downright: 8>)\n",
1075 "zings (True, 10, 14, <Direction.upleft: 5>)\n"
1076 ]
1077 }
1078 ],
1079 "source": [
1080 "width, height, grid, words = read_wordsearch('wordsearch04.txt')\n",
1081 "for w in words:\n",
1082 " print(w, present(grid, w))"
1083 ]
1084 },
1085 {
1086 "cell_type": "code",
1087 "execution_count": null,
1088 "metadata": {
1089 "collapsed": true
1090 },
1091 "outputs": [],
1092 "source": []
1093 }
1094 ],
1095 "metadata": {
1096 "kernelspec": {
1097 "display_name": "Python 3",
1098 "language": "python",
1099 "name": "python3"
1100 },
1101 "language_info": {
1102 "codemirror_mode": {
1103 "name": "ipython",
1104 "version": 3
1105 },
1106 "file_extension": ".py",
1107 "mimetype": "text/x-python",
1108 "name": "python",
1109 "nbconvert_exporter": "python",
1110 "pygments_lexer": "ipython3",
1111 "version": "3.5.2+"
1112 }
1113 },
1114 "nbformat": 4,
1115 "nbformat_minor": 0
1116 }