From d96d2490a3710c19e3be0f38448d61c3e68a096e Mon Sep 17 00:00:00 2001 From: Peter Norvig Date: Sun, 8 Aug 2021 00:28:21 -0700 Subject: [PATCH] Add files via upload --- ipynb/RaceTrack.ipynb | 386 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 ipynb/RaceTrack.ipynb diff --git a/ipynb/RaceTrack.ipynb b/ipynb/RaceTrack.ipynb new file mode 100644 index 0000000..3787b1f --- /dev/null +++ b/ipynb/RaceTrack.ipynb @@ -0,0 +1,386 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Peter Norvig
August 2021
\n", + "\n", + "# RaceTrack\n", + "\n", + "From 538's [The Riddler](https://fivethirtyeight.com/features/can-you-zoom-around-the-race-track/) on 6 August 2021:\n", + "\n", + "> The game of [RaceTrack](https://link.springer.com/chapter/10.1007/978-3-642-13122-6_26) was published by recreational mathematician Martin Gardner in 1973. There have been a few modifications and even some neat [digital versions](https://harmmade.com/vectorracer/#) since then, and it’s high time we had a race here on The Riddler. So without further ado, welcome to the Riddler-opolis 500!\n", + ">\n", + "> You begin at the midpoint of the starting line at the bottom (the thicker border), and your goal is to circumnavigate the race track shown below in a single counterclockwise loop. You’ll be moving from point to point on the grid, without ever venturing into the gray surrounding square or the central circle. If the center is at the origin (with radius 3), the starting point in blue is at (0, -5). The starting line is between (0, -3) and (0, -7).\n", + ">\n", + "> For your first move, you can choose among nine grid points surrounding and including the starting position. From there, you have inertia. So for your second move, you again have nine possible destinations, but they are determined by your current velocity vector. That is, you can maintain your current direction and speed, or you can alter your destination by one point in any direction (horizontally, vertically or diagonally). For example, if your first move was up and to the right, then your possible second moves are shown below, although two of them will cause you to crash into the wall.\n", + ">\n", + "> ![](https://fivethirtyeight.com/wp-content/uploads/2021/08/Screen-Shot-2021-08-04-at-10.44.11-PM.png?w=400)\n", + ">\n", + "> How quickly can you navigate the track? At no time can your path venture into the wall. (Being tangent to the wall is allowed, as is being on a grid point along a wall.)\n", + ">\n", + "\n", + "\n", + "# Defining Points and the Track\n", + "\n", + "First some imports: " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "from typing import List, Tuple, Iterator\n", + "import math" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now define a `Point` in the 2-D plane as a complex number, and a `Path` as a list of points. Then define some global variables: the points in the `track`; the `finish` line; the `start` point; the `zero` vector; and the allowable `deltas` (the nine possible changes on each move)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "Point = Vector = complex\n", + "Path = List[Point]\n", + "def X(point) -> float: return point.real\n", + "def Y(point) -> float: return point.imag\n", + "\n", + "track = {Point(x, y) for x in range(-7, 8) for y in range(-7, 8) if abs(Point(x, y)) >= 3}\n", + "finish = [Point(0, -3), Point(0, -7)]\n", + "start = sum(finish) / 2 # midpoint of finish line\n", + "zero = Vector(0, 0)\n", + "deltas = [Vector(x, y) for y in (1, 0, -1) for x in (-1, 0, 1) ]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Visualization\n", + "\n", + "I think it will be helpful to introduce a way to visualize the track and the path(s) on it:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def plot(paths=(), track=track, start=start, finish=finish):\n", + " \"\"\"Plot the track and any paths, along with the start and finish line.\"\"\"\n", + " fig, ax = plt.subplots()\n", + " plt.xticks([]); plt.yticks([])\n", + " ax.set_aspect('equal');\n", + " ax.add_artist(plt.Circle((0, 0), 3, alpha=0.2, color='k'))\n", + " ax.plot(*XY(track), 'k,' ) # grid points\n", + " for path in paths:\n", + " ax.plot(*XY(path), 'o-') # paths\n", + " ax.plot(*XY([start]), 'bD') # start\n", + " ax.plot(*XY(finish), 'b:') # finish\n", + " lengths = {len(path) - 1 for path in paths}\n", + " plt.title(f'{len(paths)} paths, lengths {lengths}')\n", + "\n", + "def XY(points): return [X(p) for p in points], [Y(p) for p in points]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is a single path consisting of the first move from the start point:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "path = [start, Point(1, -4)]\n", + "plot([path])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here are nine paths for the second move (two of which crash into the circle in the center):" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def velocity(path) -> Vector:\n", + " \"\"\"Velocity of the car at the end of the path.\"\"\"\n", + " return zero if len(path) == 1 else path[-1] - path[-2] " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot([path + [Point(path[-1] + velocity(path) + d)] for d in deltas])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Complete Paths and Legal Moves\n", + "\n", + "For our circle-in-the-middle track, I can say that a path `is_complete` if the path goes from the fourth quadrant to the first, second, third, and back to the fourth:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def Q1(p) -> bool: return X(p) >= 0 and Y(p) >= 0 # upper right\n", + "def Q2(p) -> bool: return X(p) < 0 and Y(p) >= 0 # upper left\n", + "def Q3(p) -> bool: return X(p) < 0 and Y(p) < 0 # lower left \n", + "def Q4(p) -> bool: return X(p) >= 0 and Y(p) < 0 # lower right\n", + " \n", + "def is_complete(path, regions=(Q4, Q1, Q2, Q3, Q4)) -> bool:\n", + " \"\"\"Does the path complete a tour of the regions, in order?\"\"\"\n", + " for p in path:\n", + " if regions[0](p):\n", + " regions = regions[1:]\n", + " return not regions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now I'll define the `legal_moves` that can be made. Given a path, `legal_moves(path)` yields `(Point, Vector)` tuples giving every possible new point position and velocity. Each point must be a point on the track (not inside the circle), the velocity vector can differ from the previous velocity vector only by an allowable delta, and the line from the previous final point on the path to the new point must not intersect the circle. \n", + "\n", + "I have inserted a hack here: to make sure that paths start off going counterclockwise, for the first two steps I insist that the deltas be either up or to the right or zero.\n", + "\n", + "I got the calculation for `intersects_circle` from several posts on math overflow." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def legal_moves(path, track=track, deltas=deltas) -> Iterator[Tuple[Point, Vector]]:\n", + " \"\"\"What (point, velocity) pairs can be reached in one step from this path?\"\"\"\n", + " if len(path) <= 2: # Make sure we start counterclockwwise\n", + " deltas = [Point(x, y) for y in (0, 1) for x in (0, 1)]\n", + " p, v = path[-1], velocity(path)\n", + " for d in deltas:\n", + " new_p = p + v + d\n", + " new_v = v + d\n", + " if new_p in track and not intersects_circle(p, new_p, 3, zero):\n", + " yield new_p, new_v\n", + " \n", + "def intersects_circle(p, q, r, c):\n", + " \"\"\"Does a line from p to q intersect a circle of radius r at center c?\"\"\"\n", + " if p == q:\n", + " return abs(c - p) < r\n", + " norm = (q - p) / abs(q - p)\n", + " v = abs(X(norm) * Y(c - p) - Y(norm) * X(c - p)) # Norm of the 2D cross-product\n", + " return (v < r)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Finding a Fastest Path\n", + "\n", + "I can use a breadth-first search, adding each legal move to each path until I find path(s) that are complete. \n", + "\n", + "I keep track of a `frontier`, which is a mapping of `{(point, velocity): path}` for all paths of the longest length explored so far. The reason for this is that there may be an exponential number of paths that arrive at the some position with the same velocity in the same number of moves. But none of them will have any advantage in reaching the finish line ahead of the others, so I only need to keep one of them. In the end, I return the completed paths I have found, but there may be many other paths that are not returned, because I only kept one for each `(point, velocity)` pair along the way. " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def search(path=[start]) -> Path:\n", + " \"\"\"Find the shortest possible complete path(s).\"\"\"\n", + " frontier = {(path[-1], velocity(path)): path}\n", + " completed = []\n", + " while not completed:\n", + " frontier = {(p, v): path + [p] \n", + " for path in frontier.values() \n", + " for p, v in legal_moves(path)}\n", + " completed = [path for path in frontier.values() if is_complete(path)]\n", + " return completed" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot(search([start]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To give you an idea of some of the paths that were not shown, here are completions of a path called `tight`:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOsAAAD7CAYAAACL3GNOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAgAElEQVR4nO3deXhb1Z3/8ffR7l12vATHBCdpVuKENAsQtsBQkhR4ylAodKFAgU7bKWlhoKQptLS/MoRCKUM70I0B0oWW7QelbZrCNBRCAllIyB4WZ3XiLfES25Kt5cwfkpwrW7ZlWbJ0ne/refTEufeee4+Wj87R0T26SmuNECLzWdJdASFEfCSsQpiEhFUIk5CwCmESElYhTELCKoRJSFhTQCl1r1LqtynY7z6l1MXJ3m8cx61USmmllC2J+7xVKXVYKfVeEvf5nlLqiFLqtmTtM5OM2LAqpdp63AJKqZ+m4DgLlFKHkr3fdBqmN4V7ga9prWcajjtdKbVKKdWolIo6AUAp5VRKPaGU2q+UOq6U2qyUWmzcJryvW4DvpbjuaTFiw6q1zo3cgDLAAzyX5mqJE4qA7T2W+YBngZtibG8DDgIXAAXAPcCzSqnKHtttBwqUUtZkVjYTjNiw9nAVUA+8GWulUuoGpdRbSqmfKqValFK7lVL/Ylh/o1JqV/gdvVop9W/h5TnASqDc0IKXh4s5lFIrwmV2KKXmGPZ3l1KqJrxuj/FY8VJKWZRSS5VSHymljiqlnlVKFYXXRbqt1yulDoRbqu8YymYppZ5WSjWF79e3Ir0DpdRvgLHAK+H78y3DYT/fx/7mKaU2KqValVJ1SqmHB6h7JEhB43Kt9R6t9RPAjp5ltNbtWut7tdb7tNZBrfWfgb3A7B6bRvaZtC57xtBaj/gb8A/g3n7W3wD4gdsAO3AN0AIUhddfCkwAFKF39g7g4+F1C4BDPfZ3L+AFPglYgfuBt8PrJhNqIcrD/68EJsR5P/YBF4f//ibwNlABOIFfAM8Y9qmBXwFZwEygE5gaXr8c+CdQGC6/1XgfjMeJc3/rgOvCf+cCZw1wPxaFH5+cPtZ/LPTS7HcfZeF9TOmxPCu8/LJ0v+6S/jpOdwVSfgdDrUQAGNfPNjcAhwFlWLY+8gKMsf1LwDfCf/cV1tcM/58GeMJ/f4xQK38xYB/kfTGGdRfwL4Z1pxDqRtoM4arocX+uDf9dDSw0rLs5zrD2tb83gO8DxXHch43hfS3pZ5t+w0roDfU14Bd9rL+VUAu7Jd2vv2TeToZu8BeBNVrrvQNsV6PDz3TYfqAcQCm1WCn1tlLqmFKqmVCLWTzA/moNf3cALqWUTWv9IaFW8V6gXin1B0PXeTBOA/6/Uqo5XKddhN6UyvqpQ27473JCrXuE8e/+9LW/m4BJwG6l1Aal1GX97GMucC1wr1LKHudxuymlLMBvgC7g6zHW2wi9cVwHzBrs/jPZyRLWp+PYboxSShn+PxY4rJRyAi8ADwFlWms38FdCXWIItRKDorX+vdb6XEKB08ADg90HoYAt1lq7DTeX1romjrJHCHV/I07tWcXBVERr/YHW+rNAKaH78nz483ysbTWhnkkhod5A3MLPzxOE3pA+rbX2xdisLLzvl3q8+ZreiA6rUmo+MIb4RoFLgSVKKbtS6mpgKqFQOgh9JmwA/OGvCy4xlKsDRimlCuKs02Sl1EXhNwEvoVHqQHjdgp5fWfTj58B9SqnTwmVLlFKfirPss8C3lVKFSqkx9G6h6oDxce4LpdQXlFIlWusg0BxeHOhre611Z/hPR4/9KKWUK7JcKeUKP04RjxN6Xi7XWnv62H2kte7sY71pjeiwAtcDL2qtj8ex7TvARKARuA+4Smt9NFx2CaEXeBPwOeBPkUJa693AM0B1uEs6UJfWSWiAp5FQt7IUWBZedyqhwZp4/Fe4Hn9XSh0nNNh0ZpxlfwAcIjSa+hrwPNEv7vuBu8P354449rcI2KGUagvX61qttXeAMprer7/TCL15RUaDPcAegPCb0r8BZwC1htH3z/fYR8yR5pFAjbCeQkKUUjcAN4e7pumsx6+B57TWq4b5uF8lFLALhvGYh4Fvaa2TeqaXUuozwM+01qXJ3G8mGHnfRZmY1vrm4TiOUuoUQt3cdYR6E/8B/Gw4jm2wFPieUuo2rXXP70oTopTaROiEibuSsb9MI2E9OTkIfS87jtBnzD8Ajw1nBbTWK4AVSd5nUkKfqaQbLIRJjPQBJiFGDAmrECYxqM+sxcXFurKyMkVVEUJs2rSpUWtdEmvdoMJaWVnJxo0bk1MrIUQvSqn9fa2TbrAQJiFhFcIkJKxCmISEVQiTkLAKYRISViFMQsIqhEmkJKzRP7gg5cxWLh3HlHIDS0lYE50cIOUyo1w6jinlBibdYCFMQsIqhElIWIUwCQmrECYhYRXCJCSsQpiEhFUIk5CwCmESElYhTELCKoRJSFiFMAkJqxAmIbNupFxGHFPKDUxm3Ui5jDimlBuYdIOFMAkJqxAmIWEVwiQkrEKYhIRVCJOQsAphEhJWIUxCwiqESUhYhTAJCasQJiFhFcIkJKxCmITMupFyGXFMKTcwmXUj5TLimFJuYNINFsIkJKxCmISEVQiTkLAKYRISViFMQsIqhElIWIUwCQmrECYhYRXCJCSsQpiELd0VEMn3ypLlTHJUkW3NoyNwnPe7tnH5o0tTXlaklrSsI8wrS5ZT5ZpHji0fpRQ5tnyqXPN4ZcnylJYVqZeSllUpldCJy1Ju6OUmOaqwWexRy2wWO1Wuuay79al+y1ZlzY1ZdpKjKiV1lXKDk5KwmmVGw0gr19XZSbY1L+Y6q7Ljdozqt7xV2WMu72ufsWTqY2PWckbymXUE6OrsZNV3H2Fs5wRGucpibtMROM7UH1/e73723PEXcmz5McuK9JPPrCbW1dnJK3c9wK67XmGWmo/LlsXe1j34g76o7fxBH+93bRtwf+93betVVmvNRx27klpvkRgJqwnFCunWtrcpvv3jnPfYzWzzrqfd34rWmnZ/K9u86+Ma0b380aVRZT3+dvzax4Tsqby54plhuGeiP2owfek5c+bojRs3prA6oj89u7vt/lY+8u7kzGVfYFT5KSk55srljzLp6FT8uov6Ga2c98XPpuQ4IkQptUlrPSfmOglr5ktHSI0ksMNHwmpS6Q6pkQR2eEhYTSaTQmokgU09CatJZGpIjSSwqSVhzXBmCKmRBDZ1JKwZymwhNZLApoaENc16zmTZ492KyrGYMqRGxsB+1LGLCdlTZbbOEElY0ygyk8V4grzWQZSymDakRiuXP8qUY6djU/aoS0T4g764T8YQJ/QXVpl1k4ZZMEpZ6Ax4KL7940wuvzRpx/P7/fh8Pvx+P1prCgoKaG1tRSmFzWbDbrdjtVrjqne8x1y8dAkf3PE37DZH1HKZrZOcckYy6ybF5fqaseKwuOJuTXser6uri46ODtrb2zl27BgtLS10dHQQDAajtlu5ciVvvfVW1AvFZrORlZWF2+2mqKiInJwcsrOzsdmiXwqDuY8ua3bM5TJbZ+jljGTWTYp1BI4PeSaL1pq2tjaOHj1KTU1Nd2uplMLpdOJ0OiksLMRiGfhU70AggN/vp66ujoMHD3YvLywspKKiArfbTU5OTtx1i9wXma2TehLWFNvj3cqsnPkodSJIkVkwk+m/C+zxeDh8+DD79++nq6sLpRS5ubmUlpYmXB+r1YrVasXpdHYv01rj8XjYvn07wWCQnJwcxo0bR2lpadR2fXm/axtVlujP5fHeRxE/CWuKKZel+zOqw+IacKRUa01TUxP79u2jrq4Oi8WC2+2moKAgdXVUiuzsbLKzQ93Zzs5Odu7cyc6dO6moqKCioqLf41/+6NKoEW+AXcc3c/njMriUTBLWFOrq7OS0wETadAuj7zoTd2kJQJ+tTVNTE7t376a5uZmsrCxKSkqSchHewYp0rYPBILW1tRw4cICysjImTpxIXl7sz6GRN593V66iaLWdfGvRcFb5pCDzWVNo1XceochZwkddO7uDGsvx48fZuHEj69ato6uri9LSUvLy8tISVKNIq15aWkpzczNr1qxh+/bteDyePst8fPFC9rbv4dSc8az+xf8MY21HPmlZU8TYqp5zz40xtwkEAuzbt4/3338fl8tFWVnsn2TJBAUFBWitqa2tpaamhtNPP50xY8bEfEMpuGw8wdUBHNvS+2Yz0kjLmiIDtaqtra2sW7eODz74gFGjRpGf33s0NdMopSgsLMTtdrN161Y2bdpER0dHr+2kdU0NCWsKdLeqvt6tqtaa/fv389ZbbxEMBikpKYn7RIVMYbPZKCsro6WlhTVr1lBfX99rm4LLxhPU0romk4Q1BfpqVQOBADt37mT79u3dJySYmdvtJi8vjw0bNlBdXR31xb+0rsknYU2yvlpVr9fLxo0bOXjwIGVlZb3OGDIrh8NBSUkJu3fvZuvWrfj9/u510roml4Q1yWK1qh6Ph/Xr19PW1pa2r2NSyWq1UlZWRm1tLe+++y4+X+jnTKV1TS4JaxLFalUjQQ0EArjd7jTXMLWKi4tpbm6OCqy0rsmTkrAm2nKYvVzPVtXr9bJhwwaCwaApRnuTYdSoUbS0tLB582b8fn/crWumPIeZVs4oJWE1y4yGZJbr2ar6/X42b96Mz+c7aYIaMWrUKJqamtixY0doql4crWsmPIeZWM5IusFJYmxVC0qK2bVrF8ePHx/xXd++FBcXU1NTw969e+Wza5JIWJOgZ6u6b98+Dh48SFHRyX1+bHFx6E2rvr5ePrsmgYQ1CYytqrbb2LVrF8XFxSNu1HewrFYrRUVFbNmyhSkXnCet6xBJWIfI2Kqetew6tm7dSn5+vunOSkoVh8OB1Wpl586dFFw2TlrXIZCwDpGxVW1oacHr9ZKVlZXuamUUt9tNfX09pVXTpXUdAgnrEBhb1apvXEl1dfVJ/zm1L6NGjWLHjh3kLRorrWuCJKxDYGxVD9XXk5ubG9fvIJ2MbDYbVquVrPGV0romSF5ZCTK2qlO/djktLS2mPzE/1dxuNwcOHCDrExXSuiZAwpqgqFa1oeGkO/EhEZFfY3SMHSOtawIkrAkwtqqTvnIp7e3tuFyudFfLFAoKCqirq8P1iTHSug7SyJinNUyMv+BX5CyhunU3tpaiPn9ETMTmcDjIHzuWva++wYTcabx/x0qyrDlyjZwBSMsap8g1a3Js+d0nO4zNnUDdb1fLVzWDlJ+fz6FDh+jQx1Eosm25KKXIseVT5ZrHK0uWp7uKGUlm3cQp1jVrbBY707LPSOjYJ7PIiPnHsk/v9Rwkco2cRJilnJHMuolTX9dtGcz1XMQJeXl5SXlMM/k1k4xyRtINjlNf122R67kkxul0ymM6SBLWOL3ftQ1/0Be1zB/0scvzXppqZH47O7bEfEzf79qWphplNglrnC5/dCnbvOvxB31orWn3t7Kl/W1O/dLCdFfNtIo/ez5b2t9Ga939mMoFmPsmVz4fpPVLfku2LYdDl4SuRSMSp7WmsbGRqaudfHR8Fxc99rV0Vynt+rvyubSsIm1O9vm+gyVhTZDdbh94IyGSSMKaIDm9MDkcDke6q2AaEtYExXNFcDEwedOLn4Q1QfJ5KzlGymVEhoOEVaTVid+qGvoZPiOdhFUIk5CwCmESElYhTEKmyImMkOisFLO8ZmSKXJrKieRL9MVslteMTJET4iQiYRXCJCSsQpiEhFWkVTAYDP8lg30DkbCKtPL7/emugmlIWBPU2dmZ7iqMCPI4xk/CmiCv15vuKowIEtb4SVgT1NXVle4qjAgnPrOKgUhYEyQnVCSHnEUWPwlrgqxWK4FAIN3VMDWv1yvXCRoECesgPPrc7d2zLh84cBsrtzyW1vqYXVtbG43PvIEFKxPzprPnjr/IdW76IWGN06PP3c5v2lZ1/7/ebuHP9g2sfE8Cm6iOlzcyM/tMlFJyYao4yKybOL3csgqvJfrh8losrA6+ndCxT3Y+n49p2WfEvNiXXJgqNpl1E6cGW+wHu6/lon+tra1yYapBkm5wnEr8sR/svpaLvmmtCQQCdATaYq6XC1PFJmGN06cKFuLq8Z2gPag5X8+TL/YHqa2tjdGjR3PUW9drnVyYqm8S1jgtufphrstd2H26uVVrCgOaGy+5l9bW1rTWzWw8Hg82TwelWeU0dTbS7m+VC1PFQcI6CEuufhilQg/ZF7MuoN5u4dX3nsThcMgZTXFqa2ujsLCQI7/bgMuazYHCvUx+6FJOfeB8Jj90qQS1HxLWBH3lUz/iFJ/mT3UvMvFjH6OpqSndVTKF9vZ2cnWQ8dlTqe04yKXfvT3dVTINCWuCsl05LMq7gGqH5tX3niQnJ0dO7h9Aa2srpaWlHP7telzWbOqKa9NdJVORsA5BpHV9ufYFJk+aRHNzc7qrlLECgQBer5fcgLSqiZKwDoGxdf3rxl8xZswY6Q734ejRo0yaNIkDK9ZKq5ogCesQnWhdn2fypElorWWwqYe2tjby8/NRx49LqzoEEtYhMrauz/zjIWbMmEFTU5NMoQvz+/10dHRQVVXFh796XVrVIZCwDsLau5+mxDmaAvso9tzxF9be/TRgaF2PPE9JcTGVlZU0Njamubbpp7WmsbGRadOm0bR/v7SqQyRhjdPau5+mvOtUbBZ79wyR8q5TWXv301Gt61Mr72Py5MkUFRWd9ANOjY2NVFZWMnbsWPb8/H+lVR0imXUTp1He4pgzREZ5i4Ho1lUBM2fOxGKx0N7enlDdzK65uZnCwkKmTJnCge3bB2xVM/m5T2c5I5l1E6eBZoj0bF2dTidz5syhs7MTj8eTUP3MqrW1FbvdzsyZM7FarXG1qpn83KeznJF0g+PU10wQ43Jj6xoMBMjLy2PevHl0dHScNIFtbW3FYrEwd+5cXC4X+7dtk8+qSSJhjdNRVyP+oC9qmT/o46jrxEBSz9YVoKCggDPPPBOPxzPiu8TNzc1YrVbmzZtHVlYWgHxWTSIJa5zm//B6DjsOds8Q0Vrz3rG1zP/h9VHb9WxdIRTYs846i0AgMCIHnSKjvtnZ2VFBlVY1uSSsgzD/h9cz+aFLGX3b7PCAQSfrf/XHqG1ita4AeXl5zJ8/n7y8PBoaGkbM97CBQID6+nrKy8u7u74R0qoml4Q1AbaybCxFTk7Nmcb61X/vtT5W6wrgdDqZPXs2Y8eOpb6+3vRnOnk8nu7vUadPn47NZuteJ61q8klYE6CUImdWGSWuMSja4m5dIfR7w9OmTWP27Nm0tbWZ8lziYDBIY2MjgUCAs88+m8rKyl5fTUirmnwS1gRlVxWjlKIi5/RBta4RZWVlnH/++RQXF1NbW2ua6XVtbW00NDRw2mmncc4551BYWNhrG2lVU0PCmiBbWTa20iwmFp5BZ6BuUK1rhNPpZObMmcydOxe/35/RXWOv10t9fT0Oh4P58+czZcqUqG6vkbSqqSFhTZBSiqyqEvKthWTZRiXUukb2U1payrnnnsuMGTPweDw0NDRkzI+wdXR0UF9fTyAQYM6cOZx11lm43e4+t5dWNXUkrEOQXVUMGqpOOS/h1jXCarUyZswYzj//fKZPn47P56O+vp7W1tZhHzkOBoM0NzdTX1+P1Wpl1qxZnHvuuZSUlAx42py0qqmjBvNCmDNnjt64cWMKq2MuWmvqfrIJnBZ+/+b9OCz5fP33T0Rt0+FtZ+Hv5tFmVQQI/c7wpwoWsuTqhwfcd3NzM/v27aOuLvSTnU6nk5ycHKxWa9Lvi9/vp62tja6uLqxWKxUVFVRUVJCfnx9X+VeWLGeSYwbZ1lwC2i+/UpggpdQmrfWcmOskrEPT8up+jv/jAO92vsUHNWs47+LrmHfLNd3rH33udp5q/zs+Q4vkCga5LnfgwEZ0dXXR0tLCkSNHqKurw+/3Y7FYsNvtuFwunE7noOqstaazsxOv14vP50NrjcPhoLy8nNLSUgoKCvr8PBrLK0uWU+WaFzXRwR/0SWAT0F9Y439GBnfAhLpuZiyXXVXM8f89wLmLr+LDJzaxfvXfo8L6cssqfPbe18h5uWUVS+I8ntPpRGtNSUkJgUCA9vZ2Ojo6aGpq4tixYzQ0NMQst3jxYlauXBmz/nl5eVRUVFBYWEh2djY5OTlRXdzBPDaTHFVJuWaN2Z774ShnlJKwmmVGQzLKRUaFA3s7OK28in0161n/qz92BzYZ18gxHs9qtZKfn09+fj6jR48GQp8x/X4/XV1d+Hw+/H4/V1xRwPz5ncye3YJSCpvNht1u774N9NkzmTOS4mHG5344yhnJANMQRUaFO/e2sPAbX0GprKiR4f6ukfPZX77NO9VHh1wHi8WCw+EgNzeXwsJCSkpKuOUWB7fc4qCkpITi4mLcbjc5OTk4HI6kXm387WdfQBOMuU6uWZNcEtYkiIwKc9jPaeVVUSPDsa6Ro7TmxlZFTX0D1/zy7aSF1uiGG0K3VHr72RcoeMdJUAcJBP1R6+SaNcknYU2CSFfYs62RxctujWpdI9fIKfUFUVqTH9BopfCpVl4/5b/5/qJKPmxoS3pofb7QLVUiQXVaXezI3cJW7ztyzZoUk9HgJImMCp+y7Exe+t5y9tWs7zUyDKHPLt9c/U3WHHqD5w7VMH70bLzX/IHfbT7Gz//5EQ3HOzl7/Ci+efFEzhw/KuH6LFgQ+vf11xO/T30xBnVX7lY5+SGJ+hsNlpY1SSJdYc/23q2rkVKKe86+hyxHDvdMmk3g4Du4/ngtN80t4c1vXcg9l01LSkt7882hW7JJUNNHwpokxq5wdnFhr8+uRsVZxSybt4yt7YdYcc4NcPAd+N3VuIIebjp3XFJC+4UvhG7JJEFNLwlrkhhHhQPHu/ptXQEWj1vMRadexM+O/JPqT97fHVg623DZrUMObUdH6JYsEtT0k7AmUXdXeMfArWt3d9iexT31bxC48pdRgQWGFNpPfjJ0SwYJamaQsCaRrSwbW0kWnq2hH1EbqHXt7g43bmWFOg6f/nWvwEJiof3qV0O3oZKgZg4JaxIppciacaIrPFDrCobu8OafUV1xRp+BhcGF9pprQrehkKBmFvnqJsl8te3UPfIu7ismkHtWOR2NTTz+718CgkAApXKYOuVsFt/7ze4yjZ5Grnj5Ck7LO40Vi1dg3fkyvHAznHomfP45cObGPJbXF+B37xzo9ZXPkRYv97/8IbUtXirK7Ny5cDJXzBoTV/1Ds2eqyLbmoQkS1EF25G6RoA4T+epmGPXsCv/zZ08DgfANtG5n567XWXnvI91lorrDO1fA9Cv7bWEj+mppb392C+/9z3TqXphDTbOHb7+4jZc21wxY98jsmRxbPkopLMqKQhFszMxfrzjZyLVuklyuZ1d41+510OvcWX94+QlR3eHm6qjAvvGVsj4DC9GhLciyEdSQP3sf+bP3AeDxBXhw1Z4B71es2TNWi23Qs2cSIeUGJte6SUE546iw1rF/hb/n8qjR4bfuIRAMdAf2/Epnvy1shMtupdUTOkc3e3It2ZNP/FrD4eaBL98hs2cyr5yRdINTwNgVViqnz+3+9tgjNNUe7v5/r+4wxN0ljih3h34NP9BhJ9Bh77W8LzJ7JvNJWFPA2BWumnwevacN28gnl91r3+DJ274SFdpe3WEYVGDvXDiZLLuVhpdm0/DSbACy7FbuXDi5zzIye8YcJKwpEukKn734SqZNXdDdwiqViy37E4yvy+JSdzmzLrmUPYbQNtcd6d0dhrgDe8WsMdx/ZRXjLjxEwbxqxrizuP/Kqj5HgyNBdcnsmYwnX92kiNaauoc3Yc1zUPLlGSeWBzUv/WQzDdXHmLvmHkoWnEnBd77NxpV/YuurKwkE/Ew77yK8c8q4Z9d/cvvs27lx+o0ndrz9xbi+1omHMag75XvUjCBf3aRBz1Hh7uUWxUVfnIK22ti7+G5aV62i5b77WfC5G7jpp79m1qLL2bP2Dap/8gxXfTidp9Y8fqI7DHG3sLW1oVtfJKjmI2FNIeOosFFBSTZn/+sEjjRn0f6lH3B81Spq7riTnNw8Lrz+lu7QFuz1ctnrJfz6R//B0cOHTuwgjsBee23oFosE1ZwkrCnU8wQJo6oLKiif6GbLkTJyb1vWHVjt85FbWMSF19/CzT99guL5MynY28lTt381evR4gMAuXRq69SRBNS8Jawr11RWGE93hYCDIlq4ZlNx1V1RgAXILi7jh1vuo/dw4dlceZ9faf0aPHvcT2EWLQjcjCaq5SVhTrK+uMJzoDh/YcZSGyZdQurR3YJVSfOfi77PrDD9bPuXijIXRo8dNxWfFDOzBg6FbhATV/CSsKdZfVxhOdIfXPPchzk9dGzOwkZMl3u3Yzv45jqiBqCdv+wp/e+MATRc+HBXYyy+HiRNh9WoJ6kghYU0xpRTWkiw6q1s4tPRNjixfT/vm+hPrDd3h1b/dQ9H118cMrPFkiXrVHDUQtWftGzz5+Iv8zX49TR+9x31X/p6d2wJ0dsInL/HzwfMVEtQRQL5nTbH2zfU0vfA+GH7sW9ktuK+cSM6s0u5lW1cf5M0/fsBFX5zC1PnlHH3qKeqXP0DewoWMeehBlN3eeyqdJXSBqramY2z40wtsfXUlx45ewoOv3YLHf+LiVVm2APcuep5vvTLECa4i5Yb9e1azzGgYjnKtq/ZFBRVA+4Kh5QbG7nBbk5dRN9zQ3cL+V2Ul2ueLfe4wdI8eT/zMU/zotZujggrg8Vv53t8+zerVqbmPUi515Yxk1k2KywWaY18Uuefy7u6wP9Qd1lp3B3ZhXn53lzjmucNhX1uSj9cf+/JFXr+NG2+MuSqmTH5MT6ZyRvKZNcWs7tiXY4y1vKAkm7PCo8O71x0BiGpha+64E/z+2OcOA08+CS6bv9d+IbT8ySeTcIdE2khYUyx/YSXK3vthtpVkxXy3nbEgujsMvQM7ylYQszt84YXwnUUryLIFovaZZQvwnUUruPDCJN85MawkrCmWM6sU95UTu1tSq9uJc5Kbzg+aaflzda/AxuoOQ+/ALqq4OGZ3+O5XvsSyRU/jsIZaWJfNz7JFT3P3K18apnssUkVGg9NAa03LX/bStqaG3HPKKbhsfK8BiPf+cVT0SocAAAQGSURBVJA1z54YHY4wjhI7/99S/vWvV/UaHQY44wzYvRtWrkRaVBMZ9iufi/4ppSi4dBwAbWtCP2TWM7AzFlRQvbmBNc99yKlTi8gtdAGhFhagfvkDACz797u4a90yVuxcETWV7rHHQv/On5/qeyOGi3SD0yQS2Nxzx9D21uFeXeK+usMQ3SWe8d+vcXH5gl7d4fnzJagjjYQ1jQYKbKzR4YgTgf07X3/ZT67FFTU6vH176CZGDglrmg0U2FijwxGRwHa99jo/Xl3Bjrr3ukeHv/710E2MHBLWDNBfYPvrDsOJwOas2coPXy3m8Y0/pbq5mgcfhAcfTMe9EakiA0wZor9Bp0h3eM2zH7B73ZGo0WE4MejE8ge4tdPB94ru5qnLfhM1OizMT8KaQfoLbF+jwxGRwM5Z/gD5P9nMimVXk9+mGV32Ab4vf4YFN313WO+LSD7pBmeYvrrEA3WHIRTY1gWzmHQEHq9eygP136aoJYD7kWd4/YkfpOHeiGSSWTcZWK5nYL//iW+gte53dDjCv3krAEtL72dp6f0AOH1g/+WzKamrlEttOaOUdIPNMqMhk8sZu8Q3czUtf66m4LLxA3aH3S2hr26munbHXJ6Kukq51JUzkm5wBovVJUbRb3e4uSA0qLTNM51tnum9lgvzkrBmuFiBzS/O6rM77PvyZ+i0w0MNd/JQw50AdNpDy4W5yWiwCcQaJa765Dg+ere+V3d4wU3f5XXg1kf+k7y2IMcKrDIaPELIrBsT6TlbR88bzbM/3ED5pEIu+/qMpAxiiPSSa92MED27xGp9LWddMT5md3jt2tBNjBzSDTaZnl3isfPLOWVCfq/u8LJloe1ffz1NFRVJJy2rCRlb2Pa1hzlnbF6v0eFf/CJ0EyOHhNWkjIH1ba7noqnuqO7w5Mmhmxg5pBtsYsYuMWtqOHN0Vnd3eNPWUHf4ggvSWEGRVBJWkzMGdvSaGqZYNKt/s5sfvzgTUPKZdQSRsI4AxsCOW1ND3f4W7pm8ASfwwdJOLHPKmHDVpPRWUgyZfGYdISKB9ZdmUWa3MLmwk8rCTrIA64ZaPnr+/XRXUQyRzLoZQeWUUvjqPQC8uc/Nm/vcANiUIrixLiXHlHKpLWcks25GWDmX1qAU51U2916eomNKudSVM5Ju8Ajj7eMdvK/lwjwkrCOMZU4Z/h7v4n6tscwpS1ONRLJIWEeYCVdNIjB3NB5CXS8PEJg7WkaDRwD56mYEmnDVJJBwjjjSsgphEhJWIUxCwiqESUhYhTAJCasQJjGo32BSSjUA+1NXHSFOeqdprUtirRhUWIUQ6SPdYCFMQsIqhElIWIUwCQmrECYhYRXCJCSsQpiEhFUIk5CwCmESElYhTOL/ADsfYJCVg5whAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tight = [start, Point(1, -4), Point(2, -3), Point(3, -1), Point(3, 1), Point(2, 3)]\n", + "plot(search(tight))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Submitting your Answer\n", + "\n", + "I'll need some help to put my answers into the form requested by The Riddler:\n", + "\n", + "> Finally, submitting your answer can be tricky work. Please be sure to submit both your total time, as well as your sequence of moves. Each move should be assigned a digit from 1 through 9, corresponding to the nine possible destinations of the move:\n", + ">\n", + "> 1 2 3\n", + "> 4 5 6\n", + "> 7 8 9" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def decode_path(path) -> List[int]:\n", + " \"\"\"The digits representing the moves (as coded changes in acceleration).\"\"\"\n", + " return [deltas.index(velocity(path[:i + 1]) - velocity(path[:i])) + 1\n", + " for i in range(1, len(path))]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[[3, 3, 1, 7, 7, 5, 7, 8, 9, 9, 3, 6],\n", + " [3, 3, 1, 7, 7, 5, 7, 8, 9, 9, 3, 9],\n", + " [3, 3, 1, 7, 7, 5, 7, 8, 9, 9, 3, 3],\n", + " [3, 3, 1, 7, 7, 5, 7, 8, 9, 9, 6, 3],\n", + " [3, 3, 1, 7, 7, 5, 7, 8, 9, 9, 6, 6],\n", + " [3, 5, 2, 4, 4, 8, 8, 7, 9, 6, 6, 3],\n", + " [3, 5, 2, 4, 4, 8, 8, 7, 9, 6, 6, 6],\n", + " [3, 5, 2, 4, 4, 8, 8, 7, 9, 6, 6, 9],\n", + " [3, 5, 2, 4, 4, 8, 8, 7, 9, 9, 3, 6],\n", + " [3, 5, 2, 4, 4, 8, 8, 7, 9, 9, 3, 9],\n", + " [3, 5, 2, 4, 4, 8, 8, 7, 9, 9, 3, 3],\n", + " [3, 5, 2, 4, 4, 8, 8, 7, 9, 9, 6, 3]]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[decode_path(path) for path in search() + search(tight)]" + ] + } + ], + "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.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}