Done worked solution for day 10
authorNeil Smith <neil.git@njae.me.uk>
Sun, 10 Sep 2017 10:55:17 +0000 (11:55 +0100)
committerNeil Smith <neil.git@njae.me.uk>
Sun, 10 Sep 2017 10:55:17 +0000 (11:55 +0100)
10-word-search/wordsearch-solution.ipynb

index 61d009756777b3cb64ff1bd1b0547a07cdf247a5..132d27fce2e90f5c9938e35c8520dab14cf3e83b 100644 (file)
     "How many vowels are left over after solving this puzzle?"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Worked solution: Part 1\n",
+    "After all the algorithmic headscratching of the previous couple of days, time for something a bit more sedate. \n",
+    "\n",
+    "## Data structures\n",
+    "This is fairly obvious. The wordsearch grid is just a grid of letters. I could store it as\n",
+    "* a list of lists of letters\n",
+    "* a list of strings\n",
+    "* a dict of letters, with key being the (row, column) pair/2-tuple\n",
+    "\n",
+    "As I'll be updating the grid in part 2, it makes sense to have something mutable, so the list of strings will be awkward. Beyond that, there's not much to choose, so I went for a list of lists of letters. \n",
+    "\n",
+    "I use the `enum` library to build a new type for directions. This makes the program a bit clearer, as well as prevents typos if I have to refer to a particular direction. The `delta` dict converts from a direction to the row and column changes that move one step in that direction. \n",
+    "\n",
+    "## Examining the grid\n",
+    "With a standard string or list, Python (along with many other languages) has the idea of a _slice_: a section of the list identified by its start position and length. The function `gslice` (for \"grid slice\") does the same thing: return a slice of grid, defined by starting row and column, length, and direction. All `gslice` does is return the cell called out by `indices`. `indices` uses the `delta` dict to convert the direction into the row and column changes, and also does the bounds checking to only return (row, column) pairs that are in the grid.\n",
+    "\n",
+    "`present_many` just does an exhaustive search of the grid, finding all possible grid slices (all rows, column, directions, and valid lengths) and checking if the slice returned is one of the given words. \n",
+    "\n",
+    "(There's another version of `present_many` that uses comprehensions to find the words, but that's slower as it keeps having to call `gslice` and `indices` for checking.)\n",
+    "\n",
+    "# Worked solution: Part 2\n",
+    "This builds on part 1. First, I find all the words. I then take all those word positions and, on a copy of the grid, replace the characters in the word position with dots, using the `set_grid` procedure. Then it's just a matter of concatenating the grid into one long string, filtering to keep only the vowels, and counting how many there are."
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": 1,
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 24,
    "metadata": {
     "collapsed": true
    },
     "            for d in Direction:\n",
     "                for wordlen in wordlens:\n",
     "                    word = cat(gslice(grid, r, c, wordlen, d))\n",
-    "                    if word in words:\n",
+    "                    if len(word) == wordlen and word in words:\n",
     "                        presences += [(word, r, c, d)]\n",
     "    return set(presences)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 30,
+   "execution_count": 25,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def present_many_c(grid, words):\n",
+    "    w = len(grid[0])\n",
+    "    h = len(grid)\n",
+    "    wordlens = set(len(w) for w in words)\n",
+    "    presences = set((cat(gslice(grid, r, c, wordlen, d)), r, c, d)\n",
+    "                    for r in range(h)\n",
+    "                    for c in range(w)\n",
+    "                    for d in Direction\n",
+    "                    for wordlen in wordlens\n",
+    "                    if len(indices(grid, r, c, wordlen, d)) == wordlen\n",
+    "                    if cat(gslice(grid, r, c, wordlen, d)) in words )\n",
+    "    return set(presences)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
    "metadata": {
     "collapsed": true
    },
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 11,
    "metadata": {
     "collapsed": true
    },
   },
   {
    "cell_type": "code",
-   "execution_count": 31,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [
     {
        "(100, 100)"
       ]
      },
-     "execution_count": 31,
+     "execution_count": 12,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 32,
+   "execution_count": 26,
    "metadata": {
     "collapsed": true
    },
   },
   {
    "cell_type": "code",
-   "execution_count": 33,
+   "execution_count": 27,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def found_words_length_c(puzzle):\n",
+    "    width, height, grid, words = puzzle\n",
+    "    return sum(len(p[0]) for p in present_many_c(grid, words))\n",
+    "\n",
+    "def total_found_words_length_c(puzzles):\n",
+    "    return sum(found_words_length_c(p) for p in puzzles)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 28,
    "metadata": {},
    "outputs": [
     {
        "8092"
       ]
      },
-     "execution_count": 33,
+     "execution_count": 28,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 34,
+   "execution_count": 29,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "1 loop, best of 3: 6.79 s per loop\n"
+      "1 loop, best of 3: 7.4 s per loop\n"
      ]
     }
    ],
   },
   {
    "cell_type": "code",
-   "execution_count": 35,
+   "execution_count": 30,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "8092"
+      ]
+     },
+     "execution_count": 30,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "found_words_length_c(puzzle)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "1 loop, best of 3: 12 s per loop\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%timeit\n",
+    "found_words_length_c(puzzle)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 32,
    "metadata": {},
    "outputs": [
     {
        "(1149, 1149, 1149)"
       ]
      },
-     "execution_count": 35,
+     "execution_count": 32,
      "metadata": {},
      "output_type": "execute_result"
     }
   },
   {
    "cell_type": "code",
-   "execution_count": 38,
+   "execution_count": 33,
    "metadata": {},
    "outputs": [
     {
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "1 loop, best of 3: 6.76 s per loop\n"
+      "1 loop, best of 3: 7.4 s per loop\n"
      ]
     }
    ],
    "source": [
     "%%timeit\n",
-    "presences = present_many(grid, words)"
+    "present_many(grid, words)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 37,
+   "execution_count": 34,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "1 loop, best of 3: 11.9 s per loop\n"
+     ]
+    }
+   ],
+   "source": [
+    "%%timeit\n",
+    "present_many_c(grid, words)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 35,
    "metadata": {},
    "outputs": [
     {
        "(1149, 1149)"
       ]
      },
-     "execution_count": 37,
+     "execution_count": 35,
      "metadata": {},
      "output_type": "execute_result"
     }
     "def unused_letters(puzzle):\n",
     "    width, height, grid, words = puzzle\n",
     "    presences = present_many(grid, words)\n",
-    "    used_words = [p[0] for p in presences]\n",
-    "    unused_words = [w for w in words if w not in used_words]\n",
+    "    used_words = [p[0] for p in presences]\n",
+    "    unused_words = [w for w in words if w not in used_words]\n",
     "    \n",
     "    unused_grid = [[c for c in r] for r in grid]\n",
     "    for w, r, c, d in presences:\n",
     "def unused_vowels(puzzle):\n",
     "    width, height, grid, words = puzzle\n",
     "    presences = present_many(grid, words)\n",
-    "    used_words = [p[0] for p in presences]\n",
-    "    unused_words = [w for w in words if w not in used_words]\n",
+    "    used_words = [p[0] for p in presences]\n",
+    "    unused_words = [w for w in words if w not in used_words]\n",
     "    \n",
     "    unused_grid = [[c for c in r] for r in grid]\n",
     "    for w, r, c, d in presences:\n",
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.5.2+"
+   "version": "3.5.3"
   }
  },
  "nbformat": 4,