{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import string\n",
    "import re\n",
    "import random\n",
    "import collections\n",
    "import copy\n",
    "\n",
    "from enum import Enum\n",
    "Direction = Enum('Direction', 'left right up down upleft upright downleft downright')\n",
    "    \n",
    "delta = {Direction.left: (0, -1),Direction.right: (0, 1), \n",
    "         Direction.up: (-1, 0), Direction.down: (1, 0), \n",
    "         Direction.upleft: (-1, -1), Direction.upright: (-1, 1), \n",
    "         Direction.downleft: (1, -1), Direction.downright: (1, 1)}\n",
    "\n",
    "cat = ''.join\n",
    "wcat = ' '.join\n",
    "lcat = '\\n'.join"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# all_words = [w.strip() for w in open('/usr/share/dict/british-english').readlines()\n",
    "#             if all(c in string.ascii_lowercase for c in w.strip())]\n",
    "# words = [w for w in all_words\n",
    "#          if not any(w in w2 for w2 in all_words if w != w2)]\n",
    "# open('wordsearch-words', 'w').write(lcat(words))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ws_words = [w.strip() for w in open('wordsearch-words').readlines()\n",
    "#             if all(c in string.ascii_lowercase for c in w.strip())]\n",
    "# ws_words[:10]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "ws_words = [w.strip() for w in open('/usr/share/dict/british-english').readlines()\n",
    "            if all(c in string.ascii_lowercase for c in w.strip())]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def empty_grid(w, h):\n",
    "    return [['.' for c in range(w)] for r in range(h)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def show_grid(grid):\n",
    "    return lcat(cat(r) for r in grid)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "..........\n",
      "..........\n",
      "..........\n",
      "..........\n",
      "..........\n",
      "..........\n",
      "..........\n",
      "..........\n",
      "..........\n",
      "..........\n"
     ]
    }
   ],
   "source": [
    "grid = empty_grid(10, 10)\n",
    "print(show_grid(grid))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def indices(grid, r, c, l, d):\n",
    "    dr, dc = delta[d]\n",
    "    w = len(grid[0])\n",
    "    h = len(grid)\n",
    "    inds = [(r + i * dr, c + i * dc) for i in range(l)]\n",
    "    return [(i, j) for i, j in inds\n",
    "           if i >= 0\n",
    "           if j >= 0\n",
    "           if i < h\n",
    "           if j < w]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def gslice(grid, r, c, l, d):\n",
    "    return [grid[i][j] for i, j in indices(grid, r, c, l, d)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def set_grid(grid, r, c, d, word):\n",
    "    for (i, j), l in zip(indices(grid, r, c, len(word), d), word):\n",
    "        grid[i][j] = l\n",
    "    return grid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "..........\n",
      "..........\n",
      "...t......\n",
      "....e.....\n",
      ".....s....\n",
      "......t...\n",
      ".......w..\n",
      "........o.\n",
      ".........r\n",
      "..........\n"
     ]
    }
   ],
   "source": [
    "set_grid(grid, 2, 3, Direction.downright, 'testword')\n",
    "print(show_grid(grid))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'..e.....'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cat(gslice(grid, 3, 2, 15, Direction.right))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<_sre.SRE_Match object; span=(0, 4), match='keen'>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "re.match(cat(gslice(grid, 3, 2, 4, Direction.right)), 'keen')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<_sre.SRE_Match object; span=(0, 3), match='kee'>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "re.match(cat(gslice(grid, 3, 2, 3, Direction.right)), 'keen')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "re.fullmatch(cat(gslice(grid, 3, 2, 3, Direction.right)), 'keen')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "re.match(cat(gslice(grid, 3, 2, 4, Direction.right)), 'kine')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def could_add(grid, r, c, d, word):\n",
    "    s = gslice(grid, r, c, len(word), d)\n",
    "    return re.fullmatch(cat(s), word)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<_sre.SRE_Match object; span=(0, 4), match='keen'>"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "could_add(grid, 3, 2, Direction.right, 'keen')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "could_add(grid, 3, 2, Direction.right, 'kine')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Direction.right: 2>"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "random.choice(list(Direction))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def fill_grid(grid, words, word_count, max_attempts=10000):\n",
    "    attempts = 0\n",
    "    added_words = []\n",
    "    w = len(grid[0])\n",
    "    h = len(grid)\n",
    "    while len(added_words) < word_count and attempts < max_attempts:\n",
    "        attempts += 1\n",
    "        r = random.randrange(w)\n",
    "        c = random.randrange(h)\n",
    "        word = random.choice(words)\n",
    "        d = random.choice(list(Direction))\n",
    "        if len(word) >=4 and not any(word in w2 for w2 in added_words) and could_add(grid, r, c, d, word):\n",
    "            set_grid(grid, r, c, d, word)\n",
    "            added_words += [word]\n",
    "            attempts = 0\n",
    "    return grid, added_words"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "40"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "g = empty_grid(20, 20)\n",
    "g, ws = fill_grid(g, ws_words, 40)\n",
    "len(ws)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "..pouchexecrate.....\n",
      ".hazardous....t.wive\n",
      "...sniradnam.aselcnu\n",
      "s.weeklies.rnb......\n",
      "e..lamenessse.o.....\n",
      "i.tsallab..s.a.i....\n",
      "tslim.......f.c.l...\n",
      "iwheelbase...f.tges.\n",
      "ed....llabhgihinirr.\n",
      "dw.limbs..nj.bitev.s\n",
      "te.wiltediu.ructs.e.\n",
      "elsombretv.oqes..a..\n",
      "ll..e..te.iela....m.\n",
      "ie.a..un.lheleiretoc\n",
      "ord..bi.scsp...kiths\n",
      "ts.malcentilitren...\n",
      "..i.e.sexetrov.stolb\n",
      ".r.s...ruof....htrof\n",
      "bgnihsirevopmi.....c\n",
      "....graciousness....\n",
      "40 words added\n",
      "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"
     ]
    }
   ],
   "source": [
    "print(show_grid(g))\n",
    "print(len(ws), 'words added')\n",
    "print(wcat(ws))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def present(grid, word):\n",
    "    w = len(grid[0])\n",
    "    h = len(grid)\n",
    "    for r in range(h):\n",
    "        for c in range(w):\n",
    "            for d in Direction:\n",
    "                if cat(gslice(grid, r, c, len(word), d)) == word:\n",
    "                    return True, r, c, d\n",
    "    return False, 0, 0, list(Direction)[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lameness (True, 4, 3, <Direction.right: 2>)\n",
      "impoverishing (True, 18, 13, <Direction.left: 1>)\n",
      "plasters (True, 14, 11, <Direction.upright: 6>)\n",
      "wilted (True, 10, 3, <Direction.right: 2>)\n",
      "toilet (True, 15, 0, <Direction.up: 3>)\n",
      "forth (True, 17, 19, <Direction.left: 1>)\n",
      "coterie (True, 13, 19, <Direction.left: 1>)\n",
      "hazardous (True, 1, 1, <Direction.right: 2>)\n",
      "abutting (True, 15, 4, <Direction.upright: 6>)\n",
      "chequing (True, 14, 9, <Direction.upright: 6>)\n",
      "weeklies (True, 3, 2, <Direction.right: 2>)\n",
      "sombre (True, 11, 2, <Direction.right: 2>)\n",
      "execrate (True, 0, 7, <Direction.right: 2>)\n",
      "mastiffs (True, 12, 18, <Direction.upleft: 5>)\n",
      "boilers (True, 3, 13, <Direction.downright: 8>)\n",
      "uncles (True, 2, 19, <Direction.left: 1>)\n",
      "centilitre (True, 15, 6, <Direction.right: 2>)\n",
      "mandarins (True, 2, 11, <Direction.left: 1>)\n",
      "wheelbase (True, 7, 1, <Direction.right: 2>)\n",
      "graciousness (True, 19, 4, <Direction.right: 2>)\n",
      "vortexes (True, 16, 13, <Direction.left: 1>)\n",
      "dwellers (True, 8, 1, <Direction.down: 4>)\n",
      "ballast (True, 5, 8, <Direction.left: 1>)\n",
      "limbs (True, 9, 3, <Direction.right: 2>)\n",
      "four (True, 17, 10, <Direction.left: 1>)\n",
      "tans (True, 1, 14, <Direction.downleft: 7>)\n",
      "highball (True, 8, 13, <Direction.left: 1>)\n",
      "wive (True, 1, 16, <Direction.right: 2>)\n",
      "broils (True, 9, 13, <Direction.downleft: 7>)\n",
      "beads (True, 11, 5, <Direction.downleft: 7>)\n",
      "mils (True, 6, 4, <Direction.left: 1>)\n",
      "reactive (True, 3, 11, <Direction.downright: 8>)\n",
      "select (True, 14, 10, <Direction.upright: 6>)\n",
      "deities (True, 9, 0, <Direction.up: 3>)\n",
      "shtik (True, 14, 19, <Direction.left: 1>)\n",
      "juveniles (True, 9, 11, <Direction.downleft: 7>)\n",
      "blots (True, 16, 19, <Direction.left: 1>)\n",
      "pouch (True, 0, 2, <Direction.right: 2>)\n",
      "brim (True, 18, 0, <Direction.upright: 6>)\n",
      "coon (True, 18, 19, <Direction.upleft: 5>)\n"
     ]
    }
   ],
   "source": [
    "for w in ws:\n",
    "    print(w, present(g, w))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def interesting(grid, words, words_limit=40, direction_slack=1):\n",
    "    dirs = set(present(grid, w)[3] for w in words)\n",
    "    return len(words) > words_limit and len(dirs) + direction_slack >= len(delta)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "interesting(g, ws)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def interesting_grid(width=20, height=20, words_limit=40, direction_slack=1):\n",
    "    boring = True\n",
    "    while boring:\n",
    "        grid = empty_grid(width, height)\n",
    "        grid, words = fill_grid(grid, ws_words, 80)\n",
    "        boring = not interesting(grid, words, words_limit=words_limit, direction_slack=direction_slack)\n",
    "    return grid, words"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dexawwt....ybossm..t\n",
      "snaprhlukulele..o.er\n",
      "...erioteb.hrevordla\n",
      "..sivc..pr.tdekcahkp\n",
      "atve..h.oiselapcy.cd\n",
      "nesrehpargohtill.pue\n",
      "n.leaveyis.smuguhhrt\n",
      "eyhsifnt.r...aeesetc\n",
      "xverbseebasedlritl.a\n",
      ".r..k.wie..mdonaiaeg\n",
      "..ac.nsof.admeeulsoo\n",
      "g.opo.cmanaotpqiinmp\n",
      "nc.us.a.lpnteaerdkiu\n",
      "ionjoys.leirinioicns\n",
      "wnkcent.skeracllkase\n",
      "oab..erusopxeao.antt\n",
      "dureifidimuhed.nskea\n",
      "aga...capitols..scrl\n",
      "h.v.sandblaster..i.i\n",
      "s.e..sdratoelhitsn.d\n",
      "61 words added;  8 directions\n",
      "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"
     ]
    }
   ],
   "source": [
    "g, ws = interesting_grid()\n",
    "print(show_grid(g))\n",
    "print(len(ws), 'words added; ', len(set(present(g, w)[3] for w in ws)), 'directions')\n",
    "print(wcat(ws))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def datafile(name, sep='\\t'):\n",
    "    \"\"\"Read key,value pairs from file.\n",
    "    \"\"\"\n",
    "    with open(name) as f:\n",
    "        for line in f:\n",
    "            splits = line.split(sep)\n",
    "            yield [splits[0], int(splits[1])]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def normalise(frequencies):\n",
    "    \"\"\"Scale a set of frequencies so they sum to one\n",
    "    \n",
    "    >>> sorted(normalise({1: 1, 2: 0}).items())\n",
    "    [(1, 1.0), (2, 0.0)]\n",
    "    >>> sorted(normalise({1: 1, 2: 1}).items())\n",
    "    [(1, 0.5), (2, 0.5)]\n",
    "    >>> sorted(normalise({1: 1, 2: 1, 3: 1}).items()) # doctest: +ELLIPSIS\n",
    "    [(1, 0.333...), (2, 0.333...), (3, 0.333...)]\n",
    "    >>> sorted(normalise({1: 1, 2: 2, 3: 1}).items())\n",
    "    [(1, 0.25), (2, 0.5), (3, 0.25)]\n",
    "    \"\"\"\n",
    "    length = sum(f for f in frequencies.values())\n",
    "    return collections.defaultdict(int, ((k, v / length) \n",
    "        for (k, v) in frequencies.items()))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "english_counts = collections.Counter(dict(datafile('count_1l.txt')))\n",
    "normalised_english_counts = normalise(english_counts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "wordsearch_counts = collections.Counter(cat(ws_words))\n",
    "normalised_wordsearch_counts = normalise(wordsearch_counts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "normalised_wordsearch_counts = normalise(collections.Counter(normalised_wordsearch_counts) + collections.Counter({l: 0.05 for l in string.ascii_lowercase}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def weighted_choice(d):\n",
    "    \"\"\"Generate random item from a dictionary of item counts\n",
    "    \"\"\"\n",
    "    target = random.uniform(0, sum(d.values()))\n",
    "    cuml = 0.0\n",
    "    for (l, p) in d.items():\n",
    "        cuml += p\n",
    "        if cuml > target:\n",
    "            return l\n",
    "    return None\n",
    "\n",
    "def random_english_letter():\n",
    "    \"\"\"Generate a random letter based on English letter counts\n",
    "    \"\"\"\n",
    "    return weighted_choice(normalised_english_counts)\n",
    "\n",
    "def random_wordsearch_letter():\n",
    "    \"\"\"Generate a random letter based on wordsearch letter counts\n",
    "    \"\"\"\n",
    "    return weighted_choice(normalised_wordsearch_counts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'aaaaaabbccddeeeeeeeeeeeffggghhhhhhiiiiiiiiillllmmnnnnnnnooooooooooppppqrrrrrrrssstttttttttuuwyyyyyyz'"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cat(sorted(random_english_letter() for i in range(100)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'aaaaaaaabbbccccddeeeeeeeeggghhhiiiiiiiijjkkllllnnnnoooooooooopppqqrrrrrssssssttttttuuuuvvwxxxxxyzzzz'"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cat(sorted(random_wordsearch_letter() for i in range(100)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'x'"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "random_wordsearch_letter()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def pad_grid(g0):\n",
    "    grid = copy.deepcopy(g0)\n",
    "    w = len(grid[0])\n",
    "    h = len(grid)\n",
    "    for r in range(h):\n",
    "        for c in range(w):\n",
    "            if grid[r][c] == '.':\n",
    "                grid[r][c] = random_wordsearch_letter()\n",
    "    return grid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dexawwtmxepybossmezt\n",
      "snaprhlukulelefaoder\n",
      "ijreriotebahrevordla\n",
      "jrsivcciprstdekcahkp\n",
      "atvecshkoiselapcydcd\n",
      "nesrehpargohtilljpue\n",
      "nuleaveyisasmuguhhrt\n",
      "eyhsifntzrmnsaeesetc\n",
      "xverbseebasedlritlla\n",
      "prcckzwiefamdonaiaeg\n",
      "osacqnsofgadmeeulsoo\n",
      "gdoporcmanaotpqiinmp\n",
      "ncmusbaslpnteaerdkiu\n",
      "ionjoyswleirinioicns\n",
      "wnkcentcskeracllkase\n",
      "oabsuerusopxeaodantt\n",
      "dureifidimuhedgnskea\n",
      "agagvccapitolsgyscrl\n",
      "hkvgsandblasterdhihi\n",
      "syenesdratoelhitsnod\n"
     ]
    }
   ],
   "source": [
    "padded = pad_grid(g)\n",
    "print(show_grid(padded))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dexawwt....ybossm..t\n",
      "snaprhlukulele..o.er\n",
      "...erioteb.hrevordla\n",
      "..sivc..pr.tdekcahkp\n",
      "atve..h.oiselapcy.cd\n",
      "nesrehpargohtill.pue\n",
      "n.leaveyis.smuguhhrt\n",
      "eyhsifnt.r...aeesetc\n",
      "xverbseebasedlritl.a\n",
      ".r..k.wie..mdonaiaeg\n",
      "..ac.nsof.admeeulsoo\n",
      "g.opo.cmanaotpqiinmp\n",
      "nc.us.a.lpnteaerdkiu\n",
      "ionjoys.leirinioicns\n",
      "wnkcent.skeracllkase\n",
      "oab..erusopxeao.antt\n",
      "dureifidimuhed.nskea\n",
      "aga...capitols..scrl\n",
      "h.v.sandblaster..i.i\n",
      "s.e..sdratoelhitsn.d\n"
     ]
    }
   ],
   "source": [
    "print(show_grid(g))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "newscast (True, 7, 6, <Direction.down: 4>)\n",
      "kittenish (True, 14, 9, <Direction.upright: 6>)\n",
      "cocks (True, 12, 1, <Direction.upright: 6>)\n",
      "lithographers (True, 5, 14, <Direction.left: 1>)\n",
      "truckle (True, 7, 18, <Direction.up: 3>)\n",
      "leotards (True, 19, 12, <Direction.left: 1>)\n",
      "they (True, 3, 11, <Direction.up: 3>)\n",
      "exposure (True, 15, 12, <Direction.left: 1>)\n",
      "dehumidifier (True, 16, 13, <Direction.left: 1>)\n",
      "sandblaster (True, 18, 4, <Direction.right: 2>)\n",
      "alien (True, 9, 17, <Direction.downleft: 7>)\n",
      "paddle (True, 12, 9, <Direction.upright: 6>)\n",
      "shadowing (True, 19, 0, <Direction.up: 3>)\n",
      "gondola (True, 9, 19, <Direction.downleft: 7>)\n",
      "wrest (True, 0, 5, <Direction.downleft: 7>)\n",
      "joys (True, 13, 3, <Direction.right: 2>)\n",
      "minster (True, 11, 18, <Direction.down: 4>)\n",
      "pales (True, 4, 14, <Direction.left: 1>)\n",
      "chairs (True, 3, 5, <Direction.downright: 8>)\n",
      "fishy (True, 7, 5, <Direction.left: 1>)\n",
      "capitols (True, 17, 6, <Direction.right: 2>)\n",
      "based (True, 8, 8, <Direction.right: 2>)\n",
      "gums (True, 6, 14, <Direction.left: 1>)\n",
      "pheromones (True, 5, 17, <Direction.downleft: 7>)\n",
      "saki (True, 16, 16, <Direction.up: 3>)\n",
      "moiety (True, 11, 7, <Direction.up: 3>)\n",
      "waxed (True, 0, 4, <Direction.left: 1>)\n",
      "guano (True, 17, 1, <Direction.up: 3>)\n",
      "thriven (True, 0, 6, <Direction.downleft: 7>)\n",
      "dilate (True, 19, 19, <Direction.up: 3>)\n",
      "moray (True, 0, 16, <Direction.down: 4>)\n",
      "icons (True, 13, 12, <Direction.downright: 8>)\n",
      "adman (True, 7, 13, <Direction.downleft: 7>)\n",
      "ukulele (True, 1, 7, <Direction.right: 2>)\n",
      "hacked (True, 3, 17, <Direction.left: 1>)\n",
      "rope (True, 5, 8, <Direction.up: 3>)\n",
      "rise (True, 12, 15, <Direction.upright: 6>)\n",
      "clue (True, 4, 15, <Direction.down: 4>)\n",
      "acted (True, 8, 19, <Direction.up: 3>)\n",
      "nicknack (True, 19, 17, <Direction.up: 3>)\n",
      "spar (True, 12, 4, <Direction.upleft: 5>)\n",
      "verbs (True, 8, 1, <Direction.right: 2>)\n",
      "boss (True, 0, 12, <Direction.right: 2>)\n",
      "annex (True, 4, 0, <Direction.down: 4>)\n",
      "neck (True, 14, 5, <Direction.left: 1>)\n",
      "repeater (True, 13, 11, <Direction.upright: 6>)\n",
      "befalls (True, 8, 8, <Direction.down: 4>)\n",
      "drover (True, 2, 17, <Direction.left: 1>)\n",
      "leave (True, 6, 2, <Direction.right: 2>)\n",
      "pans (True, 1, 3, <Direction.left: 1>)\n",
      "brave (True, 15, 2, <Direction.down: 4>)\n",
      "brigs (True, 2, 9, <Direction.down: 4>)\n",
      "opus (True, 10, 19, <Direction.down: 4>)\n",
      "live (True, 1, 6, <Direction.downleft: 7>)\n",
      "noun (True, 10, 5, <Direction.downleft: 7>)\n",
      "tail (True, 11, 12, <Direction.downright: 8>)\n",
      "riot (True, 2, 4, <Direction.right: 2>)\n",
      "care (True, 14, 13, <Direction.left: 1>)\n",
      "hits (True, 19, 13, <Direction.right: 2>)\n",
      "quilt (True, 11, 14, <Direction.upright: 6>)\n",
      "part (True, 3, 19, <Direction.up: 3>)\n"
     ]
    }
   ],
   "source": [
    "for w in ws:\n",
    "    print(w, present(padded, w))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "def decoys(grid, words, all_words, limit=100):\n",
    "    decoy_words = []\n",
    "    dlen_limit = max(len(w) for w in words)\n",
    "    while len(words) + len(decoy_words) < limit:\n",
    "        d = random.choice(all_words)\n",
    "        if d not in words and len(d) >= 4 and len(d) <= dlen_limit and not present(grid, d)[0]:\n",
    "            decoy_words += [d]\n",
    "    return decoy_words"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['friendship',\n",
       " 'stepping',\n",
       " 'featheriest',\n",
       " 'thriftily',\n",
       " 'mutilation',\n",
       " 'nook',\n",
       " 'clewing',\n",
       " 'meditated',\n",
       " 'gooier',\n",
       " 'cripples',\n",
       " 'ponderously',\n",
       " 'roundelay',\n",
       " 'curtailed',\n",
       " 'redeemed',\n",
       " 'perimeters',\n",
       " 'harelips',\n",
       " 'overcompensating',\n",
       " 'rejoicings',\n",
       " 'adobe',\n",
       " 'decreasing',\n",
       " 'interstices',\n",
       " 'curd',\n",
       " 'orientate',\n",
       " 'blueberries',\n",
       " 'juniors',\n",
       " 'broadloom',\n",
       " 'debarring',\n",
       " 'chandeliers',\n",
       " 'segues',\n",
       " 'army',\n",
       " 'snuck',\n",
       " 'pugilistic',\n",
       " 'snugs',\n",
       " 'dexterity',\n",
       " 'dallies',\n",
       " 'curving',\n",
       " 'newsletter',\n",
       " 'torn',\n",
       " 'beaching',\n",
       " 'limit',\n",
       " 'blackguards',\n",
       " 'breezier',\n",
       " 'reoccur',\n",
       " 'cabins']"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ds = decoys(padded, ws, ws_words)\n",
    "ds"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "opted (True, 5, 7, <Direction.downright: 8>)\n",
      "haywire (True, 8, 9, <Direction.downleft: 7>)\n",
      "busting (True, 4, 17, <Direction.left: 1>)\n",
      "succinct (True, 7, 17, <Direction.down: 4>)\n",
      "institute (True, 16, 18, <Direction.up: 3>)\n",
      "illicit (True, 13, 14, <Direction.left: 1>)\n",
      "hypersensitivity (True, 16, 15, <Direction.left: 1>)\n",
      "placement (True, 3, 11, <Direction.left: 1>)\n",
      "bathrobe (True, 0, 5, <Direction.right: 2>)\n",
      "inured (True, 4, 0, <Direction.right: 2>)\n",
      "caveats (True, 3, 8, <Direction.downleft: 7>)\n",
      "revisiting (True, 2, 11, <Direction.left: 1>)\n",
      "pulp (True, 15, 11, <Direction.right: 2>)\n",
      "teacup (True, 7, 16, <Direction.downleft: 7>)\n",
      "threading (True, 18, 6, <Direction.right: 2>)\n",
      "queered (True, 5, 13, <Direction.right: 2>)\n",
      "parking (True, 6, 9, <Direction.right: 2>)\n",
      "advent (True, 1, 11, <Direction.left: 1>)\n",
      "chasuble (True, 19, 19, <Direction.left: 1>)\n",
      "mosey (True, 7, 5, <Direction.downleft: 7>)\n",
      "highboys (True, 19, 4, <Direction.right: 2>)\n",
      "recharging (True, 18, 19, <Direction.up: 3>)\n",
      "flue (True, 12, 2, <Direction.upright: 6>)\n",
      "plywood (True, 3, 18, <Direction.left: 1>)\n",
      "gluing (True, 12, 12, <Direction.upleft: 5>)\n",
      "worrier (True, 1, 12, <Direction.right: 2>)\n",
      "karma (True, 17, 9, <Direction.left: 1>)\n",
      "peepers (True, 8, 7, <Direction.downleft: 7>)\n",
      "vulnerable (True, 17, 10, <Direction.right: 2>)\n",
      "boycott (True, 15, 1, <Direction.right: 2>)\n",
      "rummy (True, 5, 4, <Direction.left: 1>)\n",
      "tonic (True, 8, 13, <Direction.downleft: 7>)\n",
      "children (True, 15, 0, <Direction.up: 3>)\n",
      "reales (True, 6, 1, <Direction.right: 2>)\n",
      "rectal (True, 7, 15, <Direction.down: 4>)\n",
      "sledded (True, 14, 16, <Direction.up: 3>)\n",
      "collocates (True, 14, 5, <Direction.right: 2>)\n",
      "edict (True, 17, 4, <Direction.left: 1>)\n",
      "captor (True, 14, 5, <Direction.upright: 6>)\n",
      "amulet (True, 9, 4, <Direction.upright: 6>)\n",
      "slipper (True, 2, 13, <Direction.right: 2>)\n",
      "foot (True, 0, 0, <Direction.right: 2>)\n",
      "chef (True, 14, 4, <Direction.upright: 6>)\n",
      "goods (True, 6, 15, <Direction.right: 2>)\n",
      "salter (True, 1, 5, <Direction.left: 1>)\n",
      "crows (True, 18, 0, <Direction.right: 2>)\n",
      "paeans (True, 12, 14, <Direction.up: 3>)\n",
      "fences (True, 0, 18, <Direction.left: 1>)\n",
      "iron (True, 5, 9, <Direction.right: 2>)\n",
      "liras (True, 0, 19, <Direction.down: 4>)\n",
      "stages (True, 19, 16, <Direction.up: 3>)\n",
      "defer (True, 14, 2, <Direction.upright: 6>)\n",
      "racy (True, 5, 4, <Direction.downleft: 7>)\n",
      "gaps (True, 7, 11, <Direction.right: 2>)\n",
      "aspen (True, 12, 10, <Direction.upleft: 5>)\n",
      "rams (True, 6, 0, <Direction.downright: 8>)\n",
      "friendship (False, 0, 0, <Direction.left: 1>)\n",
      "stepping (False, 0, 0, <Direction.left: 1>)\n",
      "featheriest (False, 0, 0, <Direction.left: 1>)\n",
      "thriftily (False, 0, 0, <Direction.left: 1>)\n",
      "mutilation (False, 0, 0, <Direction.left: 1>)\n",
      "nook (False, 0, 0, <Direction.left: 1>)\n",
      "clewing (False, 0, 0, <Direction.left: 1>)\n",
      "meditated (False, 0, 0, <Direction.left: 1>)\n",
      "gooier (False, 0, 0, <Direction.left: 1>)\n",
      "cripples (False, 0, 0, <Direction.left: 1>)\n",
      "ponderously (False, 0, 0, <Direction.left: 1>)\n",
      "roundelay (False, 0, 0, <Direction.left: 1>)\n",
      "curtailed (False, 0, 0, <Direction.left: 1>)\n",
      "redeemed (False, 0, 0, <Direction.left: 1>)\n",
      "perimeters (False, 0, 0, <Direction.left: 1>)\n",
      "harelips (False, 0, 0, <Direction.left: 1>)\n",
      "overcompensating (False, 0, 0, <Direction.left: 1>)\n",
      "rejoicings (False, 0, 0, <Direction.left: 1>)\n",
      "adobe (False, 0, 0, <Direction.left: 1>)\n",
      "decreasing (False, 0, 0, <Direction.left: 1>)\n",
      "interstices (False, 0, 0, <Direction.left: 1>)\n",
      "curd (False, 0, 0, <Direction.left: 1>)\n",
      "orientate (False, 0, 0, <Direction.left: 1>)\n",
      "blueberries (False, 0, 0, <Direction.left: 1>)\n",
      "juniors (False, 0, 0, <Direction.left: 1>)\n",
      "broadloom (False, 0, 0, <Direction.left: 1>)\n",
      "debarring (False, 0, 0, <Direction.left: 1>)\n",
      "chandeliers (False, 0, 0, <Direction.left: 1>)\n",
      "segues (False, 0, 0, <Direction.left: 1>)\n",
      "army (False, 0, 0, <Direction.left: 1>)\n",
      "snuck (False, 0, 0, <Direction.left: 1>)\n",
      "pugilistic (False, 0, 0, <Direction.left: 1>)\n",
      "snugs (False, 0, 0, <Direction.left: 1>)\n",
      "dexterity (False, 0, 0, <Direction.left: 1>)\n",
      "dallies (False, 0, 0, <Direction.left: 1>)\n",
      "curving (False, 0, 0, <Direction.left: 1>)\n",
      "newsletter (False, 0, 0, <Direction.left: 1>)\n",
      "torn (False, 0, 0, <Direction.left: 1>)\n",
      "beaching (False, 0, 0, <Direction.left: 1>)\n",
      "limit (False, 0, 0, <Direction.left: 1>)\n",
      "blackguards (False, 0, 0, <Direction.left: 1>)\n",
      "breezier (False, 0, 0, <Direction.left: 1>)\n",
      "reoccur (False, 0, 0, <Direction.left: 1>)\n",
      "cabins (False, 0, 0, <Direction.left: 1>)\n"
     ]
    }
   ],
   "source": [
    "for w in ws + ds:\n",
    "    print(w, present(padded, w))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "s.esevresed...dwhims\n",
      "e.tbk..vapourse.bn.d\n",
      "h.ificembracesnslo.e\n",
      "ctr.egukiwic..ddui.r\n",
      "inor.dwh.rips.rrftsa\n",
      "reue.eeisrar.tiefiyl\n",
      "mmli.mgrg.muarth.slc\n",
      "ieft.un.a.bnbuemdole\n",
      "nn.nesimrliertseepad\n",
      "ei.imeloeccdeh.epob.\n",
      "dfsaprlrio.saf.smri.\n",
      "cnpdt.ofrn.usu..ap.h\n",
      "oom.ispeccgntlpew.sa\n",
      "tcu.e.l.lu.eda.vsgin\n",
      "e.bdsb.oarrmneloplsg\n",
      "r.olaetrleromrkr.isa\n",
      "ibatnhd.nlpoaeic.bir\n",
      "eesiee.i.luepno.o.e.\n",
      "snt.d.o.y.pcte.p.mr.\n",
      "u....jtoquesylduol..\n",
      "sfesevresedpzcdwhims\n",
      "eotbkvgvapoursehbnyd\n",
      "hiificembracesnslone\n",
      "ctrnegukiwicurdduivr\n",
      "inorydwhrripscrrftsa\n",
      "reueleeisrarvtiefiyl\n",
      "mmlinmgrgzmuarthgslc\n",
      "ieftuunyanbnbuemdole\n",
      "nncnesimrliertseepad\n",
      "eirimeloeccdehzepobm\n",
      "dfsaprlrioisafesmriq\n",
      "cnpdtsofrnausuodapxh\n",
      "oomlispeccgntlpewasa\n",
      "tcuaehlzluledakvsgin\n",
      "eibdsbeoarrmneloplsg\n",
      "rbolaetrleromrkrnisa\n",
      "ibatnhdtnlpoaeicibir\n",
      "eesieerimluepnoholey\n",
      "sntadvoaycpctespsmro\n",
      "uamapjtoquesylduoldp\n",
      "56 words added;  8 directions\n",
      "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",
      "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"
     ]
    }
   ],
   "source": [
    "g, ws = interesting_grid()\n",
    "p = pad_grid(g)\n",
    "ds = decoys(p, ws, ws_words)\n",
    "print(show_grid(g))\n",
    "print(show_grid(p))\n",
    "print(len(ws), 'words added; ', len(set(present(g, w)[3] for w in ws)), 'directions')\n",
    "print('Present:', wcat(sorted(ws)))\n",
    "print('Decoys:', wcat(sorted(ds)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "...p.mown.\n",
      ".sdse..ee.\n",
      ".e.elad.cr\n",
      "pi.dtir.ah\n",
      "rzsiwovspu\n",
      "oawh.kieab\n",
      "brow.c.rda\n",
      "ecnotops.r\n",
      "d.kc.d...b\n",
      ".staple...\n",
      "\n",
      "fhjpamownq\n",
      "wsdseuqeev\n",
      "ieaeladhcr\n",
      "piedtiriah\n",
      "rzsiwovspu\n",
      "oawhakieab\n",
      "browpcfrda\n",
      "ecnotopssr\n",
      "dikchdnpnb\n",
      "bstapleokr\n",
      "14 words added;  6 directions\n",
      "Present: apace cowhides crazies dock knows lived mown pears probed rhubarb rioted staple tops wide\n",
      "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"
     ]
    }
   ],
   "source": [
    "g, ws = interesting_grid(width=10, height=10, words_limit=5, direction_slack=6)\n",
    "p = pad_grid(g)\n",
    "ds = decoys(p, ws, ws_words)\n",
    "print(show_grid(g))\n",
    "print()\n",
    "print(show_grid(p))\n",
    "print(len(ws), 'words added; ', len(set(present(g, w)[3] for w in ws)), 'directions')\n",
    "print('Present:', wcat(sorted(ws)))\n",
    "print('Decoys:', wcat(sorted(ds)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['fickler',\n",
       " 'adapting',\n",
       " 'chump',\n",
       " 'foaming',\n",
       " 'molested',\n",
       " 'carnal',\n",
       " 'crumbled',\n",
       " 'guts',\n",
       " 'minuend',\n",
       " 'bombing',\n",
       " 'winced',\n",
       " 'coccyxes',\n",
       " 'solaria',\n",
       " 'shinier',\n",
       " 'cackles']"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "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",
    "ds = random.sample(ds_original, 15)\n",
    "ds"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['regards',\n",
       " 'perfect',\n",
       " 'instants',\n",
       " 'refined',\n",
       " 'coddle',\n",
       " 'fickler',\n",
       " 'gambol',\n",
       " 'misprint',\n",
       " 'tapes',\n",
       " 'impugns',\n",
       " 'moonshot',\n",
       " 'chump',\n",
       " 'brick',\n",
       " 'siren',\n",
       " 'faddish',\n",
       " 'winced',\n",
       " 'kielbasy',\n",
       " 'market',\n",
       " 'puckered',\n",
       " 'trains',\n",
       " 'welts',\n",
       " 'cackles',\n",
       " 'foaming',\n",
       " 'proceed',\n",
       " 'gliding',\n",
       " 'guts',\n",
       " 'uric',\n",
       " 'oaks',\n",
       " 'molested',\n",
       " 'curled',\n",
       " 'boor',\n",
       " 'solaria',\n",
       " 'gristle',\n",
       " 'bombing',\n",
       " 'loamier',\n",
       " 'ensuing',\n",
       " 'cunt',\n",
       " 'sunder',\n",
       " 'revel',\n",
       " 'coaster',\n",
       " 'grunts',\n",
       " 'mucking',\n",
       " 'typesets',\n",
       " 'carnal',\n",
       " 'whimsy',\n",
       " 'scoff',\n",
       " 'coccyxes',\n",
       " 'meanly',\n",
       " 'sprain',\n",
       " 'minuend',\n",
       " 'ringlet',\n",
       " 'fest',\n",
       " 'winced',\n",
       " 'shinier',\n",
       " 'dicier',\n",
       " 'thirds',\n",
       " 'olives',\n",
       " 'garoting',\n",
       " 'pastrami',\n",
       " 'tranquil',\n",
       " 'tamped',\n",
       " 'sunup',\n",
       " 'crumbled',\n",
       " 'throw',\n",
       " 'ridges',\n",
       " 'chaplets',\n",
       " 'curlier',\n",
       " 'lugs',\n",
       " 'collies',\n",
       " 'adapting',\n",
       " 'demeanor',\n",
       " 'deepen',\n",
       " 'lanyard',\n",
       " 'tiller',\n",
       " 'transfix',\n",
       " 'wariness',\n",
       " 'times',\n",
       " 'mitts',\n",
       " 'dowses',\n",
       " 'creels',\n",
       " 'curds',\n",
       " 'quashed',\n",
       " 'orgasmic',\n",
       " 'ibex',\n",
       " 'retraces',\n",
       " 'casino']"
      ]
     },
     "execution_count": 89,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ds_original"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "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",
      "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",
      "present_words =  ['probed', 'staple', 'rioted', 'cowhides', 'tops', 'knows', 'lived', 'rhubarb', 'crazies', 'dock', 'apace', 'mown', 'pears', 'wide']\n",
      "decoy_words =  ['fickler', 'adapting', 'chump', 'foaming', 'molested', 'carnal', 'crumbled', 'guts', 'minuend', 'bombing', 'winced', 'coccyxes', 'solaria', 'shinier', 'cackles']\n",
      "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"
     ]
    }
   ],
   "source": [
    "print('grid = ', g)\n",
    "print('padded_grid = ', p)\n",
    "print('present_words = ', ws)\n",
    "print('decoy_words = ', ds)\n",
    "print('Directions: ', [(w, '`' + str(present(g, w)) + '`') for w in ws])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# with open('example-wordsearch.txt', 'w') as f:\n",
    "#     f.write('10x10\\n')\n",
    "#     f.write(show_grid(p))\n",
    "#     f.write('\\n')\n",
    "#     f.write(lcat(sorted(ws + ds)))\n",
    "# with open('exmaple-wordsearch-solution.txt', 'w') as f:\n",
    "#     f.write('10x10\\n')\n",
    "#     f.write(show_grid(g))\n",
    "#     f.write('\\n')\n",
    "#     f.write(lcat(sorted(ws)) + '\\n\\n')\n",
    "#     f.write(lcat(sorted(ds)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "probed 3 0 6 Direction.down [(3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0)]\n",
      "staple 9 1 6 Direction.right [(9, 1), (9, 2), (9, 3), (9, 4), (9, 5), (9, 6)]\n",
      "rioted 6 7 6 Direction.upleft [(6, 7), (5, 6), (4, 5), (3, 4), (2, 3), (1, 2)]\n",
      "cowhides 8 3 8 Direction.up [(8, 3), (7, 3), (6, 3), (5, 3), (4, 3), (3, 3), (2, 3), (1, 3)]\n",
      "tops 7 4 4 Direction.right [(7, 4), (7, 5), (7, 6), (7, 7)]\n",
      "knows 8 2 5 Direction.up [(8, 2), (7, 2), (6, 2), (5, 2), (4, 2)]\n",
      "lived 2 4 5 Direction.downright [(2, 4), (3, 5), (4, 6), (5, 7), (6, 8)]\n",
      "rhubarb 2 9 7 Direction.down [(2, 9), (3, 9), (4, 9), (5, 9), (6, 9), (7, 9), (8, 9)]\n",
      "crazies 7 1 7 Direction.up [(7, 1), (6, 1), (5, 1), (4, 1), (3, 1), (2, 1), (1, 1)]\n",
      "dock 8 5 4 Direction.up [(8, 5), (7, 5), (6, 5), (5, 5)]\n",
      "apace 5 8 5 Direction.up [(5, 8), (4, 8), (3, 8), (2, 8), (1, 8)]\n",
      "mown 0 5 4 Direction.right [(0, 5), (0, 6), (0, 7), (0, 8)]\n",
      "pears 0 3 5 Direction.downright [(0, 3), (1, 4), (2, 5), (3, 6), (4, 7)]\n",
      "wide 4 4 4 Direction.upright [(4, 4), (3, 5), (2, 6), (1, 7)]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[(7, 5), (2, 3), (3, 5)]"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cts = collections.Counter()\n",
    "for w in ws:\n",
    "    _, r, c, d = present(g, w)\n",
    "    inds = indices(g, r, c, len(w), d)\n",
    "    for i in inds:\n",
    "        cts[i] += 1\n",
    "    print(w, r, c, len(w), d, inds)\n",
    "[i for i in cts if cts[i] > 1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 143,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "5\n",
      "6\n",
      "7\n",
      "8\n",
      "9\n",
      "10\n",
      "11\n",
      "12\n",
      "13\n",
      "14\n",
      "15\n",
      "16\n",
      "17\n",
      "18\n",
      "19\n",
      "20\n",
      "21\n",
      "22\n",
      "23\n",
      "24\n",
      "25\n",
      "26\n",
      "27\n",
      "28\n",
      "29\n",
      "30\n",
      "31\n",
      "32\n",
      "33\n",
      "34\n",
      "35\n",
      "36\n",
      "37\n",
      "38\n",
      "39\n",
      "40\n",
      "41\n",
      "42\n",
      "43\n",
      "44\n",
      "45\n",
      "46\n",
      "47\n",
      "48\n",
      "49\n",
      "50\n",
      "51\n",
      "52\n",
      "53\n",
      "54\n",
      "55\n",
      "56\n",
      "57\n",
      "58\n",
      "59\n",
      "60\n",
      "61\n",
      "62\n",
      "63\n",
      "64\n",
      "65\n",
      "66\n",
      "67\n",
      "68\n",
      "69\n",
      "70\n",
      "71\n",
      "72\n",
      "73\n",
      "74\n",
      "75\n",
      "76\n",
      "77\n",
      "78\n",
      "79\n",
      "80\n",
      "81\n",
      "82\n",
      "83\n",
      "84\n",
      "85\n",
      "86\n",
      "87\n",
      "88\n",
      "89\n",
      "90\n",
      "91\n",
      "92\n",
      "93\n",
      "94\n",
      "95\n",
      "96\n",
      "97\n",
      "98\n",
      "99\n"
     ]
    }
   ],
   "source": [
    "# for i in range(100):\n",
    "#     print(i)\n",
    "#     g, ws = interesting_grid()\n",
    "#     p = pad_grid(g)\n",
    "#     ds = decoys(p, ws, ws_words)\n",
    "#     with open('wordsearch{:02}.txt'.format(i), 'w') as f:\n",
    "#         f.write('20x20\\n')\n",
    "#         f.write(show_grid(p))\n",
    "#         f.write('\\n')\n",
    "#         f.write(lcat(sorted(ws + ds)))\n",
    "#     with open('wordsearch-solution{:02}.txt'.format(i), 'w') as f:\n",
    "#         f.write('20x20\\n')\n",
    "#         f.write(show_grid(g))\n",
    "#         f.write('\\n')\n",
    "#         f.write(lcat(sorted(ws)) + '\\n\\n')\n",
    "#         f.write(lcat(sorted(ds)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2+"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}