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