Add files via upload
This commit is contained in:
1 parent
3fd1948112
commit
0ce53637fd
1 file changed
+209
-138
+209
-138
@@ -22,16 +22,14 @@
|
|||||||
">\n",
|
">\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# Defining Points and the Track\n",
|
"# Defining Points and Vectors\n",
|
||||||
"\n",
|
"\n",
|
||||||
"I will start by defining the following:\n",
|
"We will need to represent 2-D points on a grid, as well as 2-D vectors for velocity and acceleration. I will use the following choices:\n",
|
||||||
"- `Point(x, y)`: a point in the 2-D plane, implemented as a complex number.\n",
|
"- `Point(x, y)`: a point in the 2-D plane, implemented as a complex number.\n",
|
||||||
"- `Path`: a list of points representing the car's positions as it moves.\n",
|
"- `Vector(x, y)`: a vector in the 2-D plane, also implemented as a complex number.\n",
|
||||||
"- `track`: all the legal grid points on the race track.\n",
|
"- `Path`: a list of points (representing the car's positions as it moves).\n",
|
||||||
"- `start`: the single starting point.\n",
|
|
||||||
"- `zero`: a zero vector (your starting velocity vector).\n",
|
|
||||||
"- `radius`: the radius of the central circle (whose center is defined as the origin).\n",
|
|
||||||
"- `X(point)`, `Y(point)`: the x- and y-coordinates of a point, respectively.\n",
|
"- `X(point)`, `Y(point)`: the x- and y-coordinates of a point, respectively.\n",
|
||||||
|
"- `zero`: a zero vector (the car's starting velocity vector).\n",
|
||||||
"- `accelerations`: the nine possible acceleration vectors (to change the car's velocity), mapped to the digit number that The Riddler uses to identify them.\n"
|
"- `accelerations`: the nine possible acceleration vectors (to change the car's velocity), mapped to the digit number that The Riddler uses to identify them.\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -44,17 +42,14 @@
|
|||||||
"import matplotlib.pyplot as plt\n",
|
"import matplotlib.pyplot as plt\n",
|
||||||
"from typing import List, Tuple, Dict, Set\n",
|
"from typing import List, Tuple, Dict, Set\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Point = Vector = complex\n",
|
"Point = Vector = complex\n",
|
||||||
"Path = List[Point]\n",
|
"Path = List[Point]\n",
|
||||||
"track = {Point(x, y) for x in range(-7, 8) for y in range(-7, 8) \n",
|
|
||||||
" if abs(Point(x, y)) >= 3}\n",
|
|
||||||
"start = Point(0, -5)\n",
|
|
||||||
"zero = Vector(0, 0)\n",
|
|
||||||
"radius = 3\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
"def X(point) -> float: \"x-coordinate\"; return point.real\n",
|
"def X(point) -> float: \"x-coordinate\"; return point.real\n",
|
||||||
"def Y(point) -> float: \"y-coordinate\"; return point.imag\n",
|
"def Y(point) -> float: \"y-coordinate\"; return point.imag\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
"zero = Vector(0, 0)\n",
|
||||||
|
"\n",
|
||||||
"accelerations = {Vector(-1, 1): 1, Vector(0, 1): 2, Vector(1, 1): 3, \n",
|
"accelerations = {Vector(-1, 1): 1, Vector(0, 1): 2, Vector(1, 1): 3, \n",
|
||||||
" Vector(-1, 0): 4, Vector(0, 0): 5, Vector(1, 0): 6, \n",
|
" Vector(-1, 0): 4, Vector(0, 0): 5, Vector(1, 0): 6, \n",
|
||||||
" Vector(-1, -1):7, Vector(0, -1):8, Vector(1, -1):9}"
|
" Vector(-1, -1):7, Vector(0, -1):8, Vector(1, -1):9}"
|
||||||
@@ -64,9 +59,14 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"# Visualization\n",
|
"# Defining the Track\n",
|
||||||
"\n",
|
"\n",
|
||||||
"I think it will be helpful to be able to visualize paths on the racetrack, so I'll define a function to do that now, even before attempting to find solutions:"
|
"I want to solve the particular problem posed by The Riddler, but I also want some flexibility to race on differently-shaped race tracks. I'll define the class `Track` as a subclass of a `set` (of `Point`s), with some additional attributes:\n",
|
||||||
|
"- `track.start`: the single starting point.\n",
|
||||||
|
"- `track.finish`: the two endpoints of the finish line.\n",
|
||||||
|
"- `track.radius`: the radius of the central circle (whose center is defined as the origin).\n",
|
||||||
|
"\n",
|
||||||
|
"The points in the track are all those points on a 15x15 square except the ones that fall inside the circle's radius.\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -75,15 +75,43 @@
|
|||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"def plot(paths=(), finish=[Point(0, -3), Point(0, -7)]):\n",
|
"class Track(Set):\n",
|
||||||
|
" def __init__(self, items, **kwds):\n",
|
||||||
|
" self.update(items)\n",
|
||||||
|
" self.__dict__.update(kwds)\n",
|
||||||
|
"\n",
|
||||||
|
"radius = 3\n",
|
||||||
|
"track = Track({Point(x, y) for x in range(-7, 8) for y in range(-7, 8) \n",
|
||||||
|
" if abs(Point(x, y)) >= radius}, \n",
|
||||||
|
" start=Point(0, -5), \n",
|
||||||
|
" finish=[Point(0, -3), Point(0, -7)], \n",
|
||||||
|
" radius=radius)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# Visualization\n",
|
||||||
|
"\n",
|
||||||
|
"I think it will be helpful to be able to visualize paths on the track, so I'll define a function to do that now, even before attempting to find solutions:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def plot(paths=(), track=track):\n",
|
||||||
" \"\"\"Plot the track and any paths, along with the start and finish line.\"\"\"\n",
|
" \"\"\"Plot the track and any paths, along with the start and finish line.\"\"\"\n",
|
||||||
" fig, ax = plt.subplots()\n",
|
" fig, ax = plt.subplots()\n",
|
||||||
" plt.xticks([]); plt.yticks([])\n",
|
" plt.xticks([]); plt.yticks([])\n",
|
||||||
" ax.set_aspect('equal');\n",
|
" ax.set_aspect('equal')\n",
|
||||||
" ax.add_artist(plt.Circle((0, 0), 3, alpha=1/3, color='k', ec='w'))\n",
|
" ax.add_artist(plt.Circle((0, 0), track.radius, alpha=1/3, color='k', ec='w'))\n",
|
||||||
" ax.plot(*XY(track), 'k,' ) # grid points\n",
|
" ax.plot(*XY(track), 'k,' ) # grid points\n",
|
||||||
" ax.plot(*XY([start]), 'ks', ms=10) # start point\n",
|
" ax.plot(*XY([track.start]), 'ks', ms=10) # start point\n",
|
||||||
" ax.plot(*XY(finish), 'k:') # finish line\n",
|
" ax.plot(*XY(track.finish), 'k:') # finish line\n",
|
||||||
" for path in paths: # paths\n",
|
" for path in paths: # paths\n",
|
||||||
" ax.plot(*XY(path), 'o-') \n",
|
" ax.plot(*XY(path), 'o-') \n",
|
||||||
" plt.title(f'{len(paths)} paths of {set(len(path) - 1 for path in paths)} moves')\n",
|
" plt.title(f'{len(paths)} paths of {set(len(path) - 1 for path in paths)} moves')\n",
|
||||||
@@ -99,14 +127,15 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"Below is my recreation of The Riddler's diagram of the first two moves. Here and throughout I'll use the convention:\n",
|
"Below is my recreation of The Riddler's diagram of the first two moves. Here and throughout I'll use the convention:\n",
|
||||||
"- `p` is a point indicating a move\n",
|
"- `p` is a point indicating a move\n",
|
||||||
" - In this example, `p` is up and right from the start to `Point(1, -4)`, so the velocity is `Vector(1, 1)`.\n",
|
|
||||||
"- `p1` is where the next point would be if the velocity remains constant.\n",
|
"- `p1` is where the next point would be if the velocity remains constant.\n",
|
||||||
"- `p2` is where the next point actually is with acceleration applied to change the velocity."
|
"- `p2` is where the next point actually is with acceleration applied to change the velocity.\n",
|
||||||
|
"\n",
|
||||||
|
"In this example, `p` is up and right from the start to `Point(1, -4)`, so the velocity is `Vector(1, 1)`."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": 4,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@@ -125,7 +154,7 @@
|
|||||||
"p1 = p + Vector(1, 1)\n",
|
"p1 = p + Vector(1, 1)\n",
|
||||||
"p2s = [p1 + a for a in accelerations]\n",
|
"p2s = [p1 + a for a in accelerations]\n",
|
||||||
"\n",
|
"\n",
|
||||||
"plot([[start, p, p2] for p2 in p2s])"
|
"plot([[track.start, p, p2] for p2 in p2s])"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -134,21 +163,21 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"# Circumnavigating the Track\n",
|
"# Circumnavigating the Track\n",
|
||||||
"\n",
|
"\n",
|
||||||
"The goal of the race is to circumnavigate the center circle. I can test for that by checking if the path goes from the fourth [quadrant](https://mathworld.wolfram.com/Quadrant.html) to the first, second, third, and back to the fourth. The predicate `is_complete(path)` does that.\n",
|
"The goal of the race is to circumnavigate the center circle. I can test if a path goes from the fourth [quadrant](https://mathworld.wolfram.com/Quadrant.html) to the first, second, third, and back to the fourth. The predicate `is_solution(path)` does that.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"<center><div><img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Cartesian-coordinate-system-with-quadrant.svg/242px-Cartesian-coordinate-system-with-quadrant.svg.png\" width=120></div></center>\n",
|
"<center><div><img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/e/e3/Cartesian-coordinate-system-with-quadrant.svg/242px-Cartesian-coordinate-system-with-quadrant.svg.png\" width=120></div></center>\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"If you wanted to generalize this to other race tracks, you could change the definition of `quadrant` (there could be more than 4 \"quadrants,\" and they could have any shape), and change the `goals` in `is_complete`."
|
"If you wanted to generalize this to other race tracks, you could change the definition of `quadrant` (there could be more than 4 \"quadrants,\" and they could have any shape), and change the `goals` in `is_solution`."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 4,
|
"execution_count": 5,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"def is_complete(path, goals=(4, 1, 2, 3, 4)) -> bool:\n",
|
"def is_solution(path, goals=(4, 1, 2, 3, 4)) -> bool:\n",
|
||||||
" \"\"\"Does the path go through all the goal quadrants in the prescribed order?\"\"\"\n",
|
" \"\"\"Does the path go through all the goal quadrants in the prescribed order?\"\"\"\n",
|
||||||
" i = 0 # Index of next goal to be achieved\n",
|
" i = 0 # Index of next goal to be achieved\n",
|
||||||
" for p in path:\n",
|
" for p in path:\n",
|
||||||
@@ -160,10 +189,7 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"def quadrant(p: Point) -> int: \n",
|
"def quadrant(p: Point) -> int: \n",
|
||||||
" \"\"\"What quadrant of the 2-D plane is this point in?\"\"\"\n",
|
" \"\"\"What quadrant of the 2-D plane is this point in?\"\"\"\n",
|
||||||
" if X(p) >= 0:\n",
|
" return (1 if Y(p) >= 0 else 4) if X(p) >= 0 else (2 if Y(p) >= 0 else 3)"
|
||||||
" return 1 if Y(p) >= 0 else 4\n",
|
|
||||||
" else:\n",
|
|
||||||
" return 2 if Y(p) >= 0 else 3"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -172,43 +198,12 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"# Legal Moves\n",
|
"# Legal Moves\n",
|
||||||
"\n",
|
"\n",
|
||||||
"I'm considering how to solve for our specific central-circle-track, and also make the code general enough that it can handle other tracks. I'll do that by splitting up responsibility for determining legal moves:\n",
|
"As stated earlier, I want to solve the specific circular track, and also make the code general enough that it can handle other tracks. I'll do that by splitting up responsibility for determining legal moves:\n",
|
||||||
"- `all_moves(path)` is general; it considers points with all 9 accelerations and returns the points that are on the track.\n",
|
"- `all_moves(path)` is general; it returns the points on the track that can be reached by applying the 9 accelerations.\n",
|
||||||
"- `circular_track_moves(path)` is specific; it limits moves in the following ways:\n",
|
"- `circular_track_moves(path)` is specific; it limits moves in the following ways:\n",
|
||||||
" - It won't make moves that stay in one place (they are legal, but wasteful and not needed on this track).\n",
|
" - It won't make moves that stay in one place (they are legal, but wasteful and not needed on this track).\n",
|
||||||
" - It won't pass from the fourth quadrant backwards over the finish line into the third (legal, but wasteful).\n",
|
" - It won't pass from the fourth quadrant backwards over the finish line into the third (legal, but wasteful).\n",
|
||||||
" - It won't let a move intersect the central circle (even if the start and end points of the move are outside the circle).\n"
|
" - It won't let a move's line segment intersect the central circle (even if the start and end points of the move are on valid grid points outside of the circle).\n"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 5,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"def all_moves(path, track=track) -> Set[Point]:\n",
|
|
||||||
" \"\"\"Moves with any allowable acceleration that end up on the track.\"\"\"\n",
|
|
||||||
" p1 = path[-1] + velocity(path)\n",
|
|
||||||
" return {(p1 + a) for a in accelerations} & track\n",
|
|
||||||
" \n",
|
|
||||||
"def circular_track_moves(path) -> Set[Point]:\n",
|
|
||||||
" \"\"\"Reasonable moves on the circular track.\"\"\"\n",
|
|
||||||
" p = path[-1]\n",
|
|
||||||
" return {p2 for p2 in all_moves(path)\n",
|
|
||||||
" if p2 != p\n",
|
|
||||||
" and not (quadrant(p) == 4 and quadrant(p2) == 3) # Don't go backwards\n",
|
|
||||||
" and not intersects_circle(p, p2)}\n",
|
|
||||||
"\n",
|
|
||||||
"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": "markdown",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"The math for solving for the intersection of a line segment and a circle looks [complicated](https://stackoverflow.com/questions/6091728/line-segment-circle-intersection), so instead I just define N+1 points along the line from point *p* to point *p2* and check if any of those points is within the radius of the circle (which by definition is centered on the origin). If N is too small this could miss an intersection, but that shouldn't be a problem for our uses."
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -217,9 +212,40 @@
|
|||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"def intersects_circle(p: Point, p2: Point, radius=radius) -> bool:\n",
|
"def all_moves(path, track=track) -> Set[Point]:\n",
|
||||||
|
" \"\"\"Moves with any allowable acceleration that end up on the track.\"\"\"\n",
|
||||||
|
" p1 = path[-1] + velocity(path)\n",
|
||||||
|
" return {(p1 + a) for a in accelerations} & track\n",
|
||||||
|
" \n",
|
||||||
|
"def circular_track_moves(path, track=track) -> Set[Point]:\n",
|
||||||
|
" \"\"\"Reasonable moves on the circular track.\"\"\"\n",
|
||||||
|
" p = path[-1]\n",
|
||||||
|
" return {p2 for p2 in all_moves(path, track)\n",
|
||||||
|
" if p2 != p\n",
|
||||||
|
" and not (quadrant(p) == 4 and quadrant(p2) == 3) # Don't go backwards\n",
|
||||||
|
" and not intersects_circle(p, p2, track)}\n",
|
||||||
|
"\n",
|
||||||
|
"def velocity(path) -> Vector:\n",
|
||||||
|
" \"\"\"Velocity of the car when it is at the end of the path.\"\"\"\n",
|
||||||
|
" return zero if len(path) == 1 else path[-1] - path[-2] "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"The math for solving for the intersection of a line segment and a circle [looks complicated](https://stackoverflow.com/questions/6091728/line-segment-circle-intersection), so instead I just define N+1 points along the line from point *p* to point *p2* and check if any of those points is within the radius of the circle (which by definition is centered on the origin). If N is too small this could miss an intersection, but that shouldn't be a problem for our uses."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 7,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def intersects_circle(p: Point, p2: Point, track=track) -> bool:\n",
|
||||||
" \"\"\"Does any point w on the line from p to p2 fall within radius of center?\"\"\"\n",
|
" \"\"\"Does any point w on the line from p to p2 fall within radius of center?\"\"\"\n",
|
||||||
" return any(abs(w) < radius for w in waypoints(p, p2))\n",
|
" return any(abs(w) < track.radius for w in waypoints(p, p2))\n",
|
||||||
"\n",
|
"\n",
|
||||||
"def waypoints(p, p2, N=20) -> List[Point]:\n",
|
"def waypoints(p, p2, N=20) -> List[Point]:\n",
|
||||||
" \"\"\"All the points that are i/N of the way from p to p2\"\"\"\n",
|
" \"\"\"All the points that are i/N of the way from p to p2\"\"\"\n",
|
||||||
@@ -233,7 +259,7 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"# A Frontier of Possible Paths\n",
|
"# A Frontier of Possible Paths\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Now I want to expand paths by adding legal moves to the end until I find complete path(s). I could keep all my paths in a `list`, but there will be an exponential number of them (up to 9<sup><i>n<i/></sup> paths of length *n* and I estimate *n* will be around 10 to 13).\n",
|
"Now I want to expand paths by adding legal moves to the end until I find solution(s)–paths that circumnavigate the circle. I could keep all my paths in a `list`, but there will be an exponential number of them (up to 9<sup><i>n<i/></sup> paths of length *n* and I estimate *n* will be around 10 to 13).\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Instead I will keep track of a *frontier*, which I define as a mapping of `{(endpoint, velocity): path}`. The idea is that there may be many paths that arrive at the some endpoint with the same velocity in the same number of moves. I only need to keep one of them, because they can all be continued in exactly the same ways.\n",
|
"Instead I will keep track of a *frontier*, which I define as a mapping of `{(endpoint, velocity): path}`. The idea is that there may be many paths that arrive at the some endpoint with the same velocity in the same number of moves. I only need to keep one of them, because they can all be continued in exactly the same ways.\n",
|
||||||
" \n",
|
" \n",
|
||||||
@@ -242,56 +268,57 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 7,
|
"execution_count": 8,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"Frontier = Dict[Tuple[Point, Vector], Path]\n",
|
"Frontier = Dict[Tuple[Point, Vector], Path]\n",
|
||||||
"\n",
|
"\n",
|
||||||
"def expand(frontier, legal_moves=circular_track_moves) -> Frontier:\n",
|
"def expand(frontier, legal_moves=circular_track_moves, track=track) -> Frontier:\n",
|
||||||
" \"\"\"The {(endpoint, velocity): path} frontier extended one move in all legal ways.\"\"\"\n",
|
" \"\"\"The {(endpoint, velocity): path} frontier extended one move in all legal ways.\"\"\"\n",
|
||||||
" return {(p, velocity([path[-1], p])): path + [p] \n",
|
" return {(p, velocity([path[-1], p])): path + [p] \n",
|
||||||
" for path in frontier.values() \n",
|
" for path in frontier.values() \n",
|
||||||
" for p in legal_moves(path)}"
|
" for p in legal_moves(path, track)}"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"# Searching for Fastest Complete Paths\n",
|
"# Searching for the Fastest Solutions\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Now all I have to do to solve the problem is repeatedly expand the frontier until some path(s) are complete. The first completed paths found are guaranteed to be the fastest (i.e., have the fewest moves), because we are expanding one move at a time. In the end, I return a list of all the completed paths in the frontier, but there may be many other paths that are not returned, because the frontier only keeps one path for each `(endpoint, velocity)` pair, and there are only so many ways to cross the finish line. "
|
"Now all I have to do to solve the problem is repeatedly expand the frontier until one or more solutions are found. The first solutions found are guaranteed to be the fastest (i.e., they are the paths with the fewest possible moves), because we are expanding paths one move at a time, and the previous time did not find any solutions. In the end, I return a list of all the solutions in the frontier, but there may be many other paths that are not returned, because the frontier only keeps one path for each `(endpoint, velocity)` pair, and there are only so many ways to cross the finish line. "
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 8,
|
"execution_count": 9,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"def search(path=[start], legal_moves=circular_track_moves) -> List[Path]:\n",
|
"def search(track=track, path=None, legal_moves=circular_track_moves) -> List[Path]:\n",
|
||||||
" \"\"\"Find the shortest possible complete path(s).\"\"\"\n",
|
" \"\"\"Find the shortest possible solution paths.\"\"\"\n",
|
||||||
|
" if path is None: path = [track.start]\n",
|
||||||
" frontier = {(path[-1], velocity(path)): path}\n",
|
" frontier = {(path[-1], velocity(path)): path}\n",
|
||||||
" complete_paths = []\n",
|
" solutions = []\n",
|
||||||
" while not complete_paths:\n",
|
" while not solutions:\n",
|
||||||
" frontier = expand(frontier, legal_moves)\n",
|
" frontier = expand(frontier, legal_moves, track)\n",
|
||||||
" complete_paths = list(filter(is_complete, frontier.values()))\n",
|
" solutions = list(filter(is_solution, frontier.values()))\n",
|
||||||
" return complete_paths"
|
" return solutions"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"# Solutions\n",
|
"# Solutions!\n",
|
||||||
"\n",
|
"\n",
|
||||||
"We are ready to solve the problem:"
|
"We are ready to solve the problem:"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 9,
|
"execution_count": 10,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@@ -306,7 +333,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"solutions = search([start])\n",
|
"solutions = search(track)\n",
|
||||||
"\n",
|
"\n",
|
||||||
"plot(solutions)"
|
"plot(solutions)"
|
||||||
]
|
]
|
||||||
@@ -315,7 +342,7 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"We see that the fastest solution is 12 moves, and there are at least 7 ways to get there (with other ways discarded).\n",
|
"We see that the fastest solution is 12 moves, and there are at least 7 ways to get there (with perhaps other ways discarded).\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# Submitting your Answer\n",
|
"# Submitting your Answer\n",
|
||||||
"\n",
|
"\n",
|
||||||
@@ -332,7 +359,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 10,
|
"execution_count": 11,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
@@ -343,7 +370,7 @@
|
|||||||
" return [accelerations[velocity(path[:i + 1]) - velocity(path[:i])]\n",
|
" return [accelerations[velocity(path[:i + 1]) - velocity(path[:i])]\n",
|
||||||
" for i in range(1, len(path))]\n",
|
" for i in range(1, len(path))]\n",
|
||||||
"\n",
|
"\n",
|
||||||
"def path_from_digits(digits, start=start) -> Path:\n",
|
"def path_from_digits(digits, start=track.start) -> Path:\n",
|
||||||
" \"\"\"The path that the digits represent.\"\"\"\n",
|
" \"\"\"The path that the digits represent.\"\"\"\n",
|
||||||
" v = zero # Starting velocity\n",
|
" v = zero # Starting velocity\n",
|
||||||
" path = [start]\n",
|
" path = [start]\n",
|
||||||
@@ -355,7 +382,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 11,
|
"execution_count": 12,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@@ -370,7 +397,7 @@
|
|||||||
" [3, 3, 1, 7, 7, 5, 7, 8, 9, 9, 3, 3]]"
|
" [3, 3, 1, 7, 7, 5, 7, 8, 9, 9, 3, 3]]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 11,
|
"execution_count": 12,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
@@ -390,7 +417,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 12,
|
"execution_count": 13,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@@ -458,39 +485,39 @@
|
|||||||
"openings = [3, 3], [3, 5], [3, 6], [6, 2], [6, 3], [3, 5, 2, 4, 4]\n",
|
"openings = [3, 3], [3, 5], [3, 6], [6, 2], [6, 3], [3, 5, 2, 4, 4]\n",
|
||||||
"\n",
|
"\n",
|
||||||
"for digits in openings:\n",
|
"for digits in openings:\n",
|
||||||
" plot(search(path_from_digits(digits)))"
|
" plot(search(track, path=path_from_digits(digits)))"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"# Tests and Visualizations\n",
|
"# Visualizations and Tests\n",
|
||||||
"\n",
|
"\n",
|
||||||
"To gain more confidence in the code, here are some unit tests and visualizations:"
|
"To gain more confidence in the code, here are some visualizations and unit tests:"
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 13,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"def test_intersects(expected: bool, segments: List[Path]):\n",
|
|
||||||
" \"\"\"Test if [p, q] segments get the expected result from `intersects_circle`.\"\"\"\n",
|
|
||||||
" plot(segments, finish=[])\n",
|
|
||||||
" for p, q in segments:\n",
|
|
||||||
" assert intersects_circle(p, q) is expected"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 14,
|
"execution_count": 14,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"def test_intersects(expected: bool, segments: List[Path]):\n",
|
||||||
|
" \"\"\"Test if [p, q] segments get the expected result from `intersects_circle`.\"\"\"\n",
|
||||||
|
" plot(segments)\n",
|
||||||
|
" for p, q in segments:\n",
|
||||||
|
" assert intersects_circle(p, q) is expected"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 15,
|
||||||
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"image/png": "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 truncated
|
"image/png": "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 truncated
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"<Figure size 432x288 with 1 Axes>"
|
"<Figure size 432x288 with 1 Axes>"
|
||||||
]
|
]
|
||||||
@@ -510,12 +537,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 15,
|
"execution_count": 16,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAOsAAAD7CAYAAACL3GNOAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAbiUlEQVR4nO3df3RcZ33n8fd3pJFGPyzJkmXLimLLlh3/wHaCrdQ1hcROugmnJgTSkG5pC+SwLD001LBbzkJbsm4aGkrLoeF0Gw60gVJCaTZLCY5ZYA9JvEuShthxiGvHji3/lKXRT0vRj5E0kp79487II3l+687cuXe+r3N0JN25z73PnZnP3Oe5c597xRiDUqrw+ZyugFIqPRpWpVxCw6qUS2hYlXIJDatSLqFhVcolNKw5IiLfEpGHc7wOEZFvisgVEflFinkfEZFeETmQwfL/VkQGReQJEdH3isM8/QKIyPMiMiEio5GfUzlaz0dE5Oe5WHYK7wT+A9BijPmVRDOJyBLgs8Adxpi7YqbvEZHnRGRYRM4vLGeMeQDYALwf2GZ35VVmPB3WiAeMMdWRnw1OV8Zmq4HzxpixFPPVR37/+4LpY8DjwGcSFTTG9AG9QEO2lVT2KIawpkVE9ovIUyLyLyIyIiKvisiNMY9/VkQ6Io+dEJH3R6ZvAr4G7IrsvYdiFrtURA5GyrwsIm2RMiIiX4k0S4dF5HUR2ZKgXs0i8sNIc/SMiHwsMv2jwN/HrPfPkmxeaeT3bOxEY8wvjDH/BJxN8fTMxiwjrkjr4oXIdg2JyFkReUdk+qXItn44Zv5aEfm2iPSJyAUR+VMR8YlIeaT8lph5G0UkJCLLI/+/R0Rei8z3oohsi5n3v4nI5chzfkpEbk+xbe5hjPHsD/A80Af0Ay8Au5PMux8IA/cCfuCPgHOAP/L4B4BmrA+438LaK62MPPYR4OcLlvctYBD4Faw3+hPA9yKP3QkcAeoAATZFlxWnXoeAvwMCwE2R7bk90XrjlBfg94HOJPP8OtYeOtHjh4AvASVJ5vkIMA3cD5QADwMXgf8BlAN3ACNAdWT+bwNPA0uAVuBN4KORxx4HvhCz7D8Afhz5ezvWnn5nZD0fBs5H1rEBuAQ0R+ZtBdqcfh/a9n52ugI53TjrBV0SeSE/HHmzxH3xImH9t5j/fUA38K4E878G3B3zRo0X1r+P+f83gJORv2+LvDl/FfAlqf/1wAywJGbaI8C3Eq03zjL6sT6E7kkyT6qw7oo8d5PA8gTzfAQ4HfP/VsAAK2KmDUQ+cEoiy9oc89jHgedj6nM25rEXgA9F/n4M+PMF6z4F3AqsiwT514l8yHrpx9PNYGPMy8aYEWPMpDHmH7Fe9N9IUuRSTNlZoBNrb4qIfCim6TUEbAGWpahCMObvcaA6suxngb/F2uv0iMjXRaQmTvlmYNAYMxIz7QJwXYr1xloO/AnwUAZlFvocVsugyhjTm2S+npi/QwDGmIXTqrGetzKsbYmK3a5ngQoR2Skiq7EC/q+Rx1YD/zX6OkRei+ux9qZngE9hffD2isj3RKQ5460tUJ4OaxwGq1mYyPXRPyJfVbQAXZE3zDeAB4AGY0wd1sGa6LIyHrpkjPmqMWYH8DbgBuIf5OkC6iNHc6NWAZczWM8sVnNzk4gk2/ZkNgEHjDHTWZZfKLq3Xx0zbW67InV+Evht4IPAMzEfWJewmsh1MT+Vxph/jpT9rjHmnZFlG+Avbaqz4zwbVhGpE5E7RSQgIqUi8jvALcBPkhTbISL3iEgp1if0JPBvQBXWC98XWfb9WHvWqB6gRUTK0qzbzZG9hh+r7zuB1dydxxhzCXgReCSyHduAj2Lt5TIxifValyyoh09EAlh9dImsI942+CPLsIUxZgYrjF8QkSWRD8P/AnwnZrbvYh0b+J3I31HfAH4/8vyJiFSJyN7IcjaIyG0iUo71nIaI87y6lWfDivUGe5irB5g+CbzPGJPsu9ansd4gV4Dfw+rnhY0xJ4AvAy9hBXMrVpM66lngOBAUkf406laD9aa7gtX8GwD+OsG8v411oKQLqyn4340x/yeNdcSKHgVe+HrfgvWG/hHWni0E/DRO+RIWHEm2wSexPqjOAj/HCuTj0QeNMS9HHm8G/nfM9MPAx7C6EVeAM1j9ZbCOTXwR6/UOYnUB/tjmejtGIh30oici+4F1xpjfdboudhORSuAtrKPhGZ28ISLXYwXi7ZEPLeUQL+9ZVYQxZhxrD/MdEflBuuVE5KtY3YCva1Cdp3vWCC/vWZU3aFiVcgltBivlEhpWpVwi6cnZCy1btsy0trbmqCpKqSNHjvQbYxrjPZZRWFtbWzl8+LA9tVJKXUNELiR6TJvBSrmEhlUpl9CwKuUSGlalXELDqpRLZHQ0WCmVmTdfDvLS0x2MDk5SXV/OrrvbuGFnU1bL0rAqlSNvvhzkuSdOMj1ljS4cHZzkuSdOAmQV2Jw0g7O9IIGWK4xyTqzTi+VeerpjLqhR01OzvPR0R1brzklYsx0coOUKo5wT6/RaueDZYUYH419cI9H0VLQZrJSNgmeHeeWZc1w8MWhdoStOtqvry7NatoZVKRvEhjRQ7WfXPW0EKkv5f0+entcULi3zsevutqzWoWFVahHihXTLLddRFrCiVeov0aPBSjkpVUijbtjZlHU4F9KwKpWBdEOaCxpWpdLgZEijNKxKJVEIIY3SsCoVRyGFNErDqlSMQgxplPM1UKoAFHJIowqnJko5wA0hjSq8GimVB24KaVROaiYiWZ0oreUKo5wT68xXucWG1InXIionYS3UkRBarnDXmY9RMHbsSZ14LaIKd5+vlA3c2NxNxH01VioNXgpplHtrrlQcXgxplPu3QCm8HdIo72yJKkrFENIo722RKgrFFNIo726Z8qRiDGmU97dQeUIxhzSqeLZUuZKG9Kri22LlChrSaxXvlrvB60/Czx6C4U6obYHbH4Rt9zldq5zSkCamz0Chev1JOPCHEA5Z/w9fsv4HTwZWQ5qajrop1HI/e+hqUKPCIWt6mmF1w6gbt42C0VE3Wu5aw52ZTV/s+mwq6/VRMDrqRl2rtsVq+i4UqAVjYBF3iHOSNnezp89Qobr9wfl9VgDxwcQQPP0A7P0y+APO1S9DGtLF02eqUEX7pbFHg2/7PAycgf/7Jeg9Ab/1Hai9ztl6pqAhtY8+Y4Vs233xDyatvBH+9ePw9Vvhvm/D6nfkv24paEjtp8+cG216Dyx7Fr73QfjHu+DdX4Sb/1NB9GM1pLmjz6BbNW6Ajz0L3//P8KM/gq7XHO3HakhzT59JNwvUwn/8Z3j+Ecf6sRrS/NFn1O18PrjtT/Lej9WQ5p8+s16Rp36shtQ5+gx7SQ77sRpS5+kz7TU292NzHdKDZw/y6KuPEhwL0lTVxL7t+9i7dq8ty/YaDasX2dCPzcee9ODZg+x/cT8TMxMAdI91s//F/QAa2DgkkxOM29vbzeHDh1Mv1CUjGoqiXO9Jqx975Xxa/diFIX37HatyNgrmjqfuoHus+5rpK6tW8tN7f2r7+txQTkSOGGPa4z2mo26KoVwa/VgnRsEEx4IZTV/s+txYLpY2g4tBkn6skweOmqqa4u5Zm6qacr5uN9KwFosF/djgV+/nlbI/5uK5WceO7u7bvm9enxUgUBJg3/Z9eauDm2hYi0yw/F28UvYkF89MEvANsevmMFs++D7KKvx5r0v0INKDLzzI1OwUK6tW6tHgJDSsReKa5u57r2PLwD9Q1vEM/OR5x84r3rt2L0+9+RQA33z3N/O+fjfRsHpc0j7p7D85el6xyoyG1aPSOnDk0HnFKjsaVo/J6uhuAY+PVVdpWD1i0V/BFNj4WHUtDavL2fo9aQGMj1WJaVhdKmcnM2g/tmBpWPPgB0cv81c/OUXXUIjmugo+c+cG3vf2whwFM8ej/Vg7X4t807Dm2A+OXuZz3z9GKDwDwOWhEJ/7/jGAjN4kjpwW6LF+rF2vhVN01E2Oy/3aF5/l8lDomullJT7evqouZfklYzMsOdHLutI6pkqhc3kpXctKmS1JvYc7dOgQt956a1r1TFZWzCz3jn6He0e/yxn/DXx56ecZLGm0bZ3ny/6aoaEhbqp8eFH1TOXoxSGmZmavmX5dXQUvfPa2tJaho248XK4rTlCBuG+aWEvGZljdPU39yCxTgTrOJgjpzMwMs7MzGGOYnTXMzs5izCzGwI4dOxgdHUVEEBF8Ph8+nw8RoaTEh89XknD9sQEw4uN/LvkQ5/zreGDor3ik/5N8ZemfcrJsS9Jymairq4OpzMtlsr5Ez3mi1ygeHXXjYc11FXH3rNfVVfAvH991zfS55u6bkfGk96yZa+6Gw2GGhoYYHh5maGiIK1euMDExQTgcZmZmJq36WEEtwe/3U1FRQX19PXV1ddTU1LB06VJ8Pl+S0rug7z1UfO+D/NmVz9rWj73/xzUAfPPd1z4fdkrUymmuq8jpeu2iYc2xz9y5YV4/CaDCX8Jn7twwb75EfVJ8swwM9NHX10cwGGR8fJxwOJx1fYwxTE9PMz09TSgUYnBwEICysjKqq6tZuXIlDQ0NNDQ0UFoa5+3h4n5suq9FodKw5lj0wEWiI5CJQhqaHOPchQ4uXLjAyMgIs7PJm82LNTU1xeDgIIODg5SWllJbW0traytNTU1UVlbOn9ml38emei0KXU4OMKnUEl0+ZXT8Lc6dO0dXVxcTExOpF5RjlZWVrFq1itWrV1NTU3PtDG88Y30f66/I+vvY+398P6CjbsCBA0wqsWR70hMn/52LFy8yOTnpdDXnjI+Pc/LkSS5cuMDatWtpbW2dv6f16PexhUjDmifJ+qQdHafp6OggFEr/qGS+hUIhjh8/zoULF9iwYQOrV6+mpCRyNNnF/Vg30bDmWLKTGfr6+jh+/Dj9/f22HNrPh9HRUY4ePUpvby+bNm2itrbWesCl/Vg30bDmSLKLine truncated
|
"image/png": "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 truncated
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"<Figure size 432x288 with 1 Axes>"
|
"<Figure size 432x288 with 1 Axes>"
|
||||||
]
|
]
|
||||||
@@ -535,12 +562,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 16,
|
"execution_count": 17,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"image/png": "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 truncated
|
"image/png": "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 truncated
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"<Figure size 432x288 with 1 Axes>"
|
"<Figure size 432x288 with 1 Axes>"
|
||||||
]
|
]
|
||||||
@@ -552,7 +579,7 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"# Waypoints on a line segment that are checked to see if they are outside the circle\n",
|
"# Waypoints on a line segment that are checked to see if they are outside the circle\n",
|
||||||
"p, q = Point(-7, 7), Point(7, 0)\n",
|
"p, q = Point(-7, 7), Point(7, 0)\n",
|
||||||
"plot([waypoints(p, q)], finish=[])\n",
|
"plot([waypoints(p, q)])\n",
|
||||||
"assert not intersects_circle(p, q)"
|
"assert not intersects_circle(p, q)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -566,7 +593,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 17,
|
"execution_count": 18,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@@ -631,7 +658,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"frontier = {(start, zero): [start]}\n",
|
"frontier = {(track.start, zero): [track.start]}\n",
|
||||||
"\n",
|
"\n",
|
||||||
"for i in range(6):\n",
|
"for i in range(6):\n",
|
||||||
" frontier = expand(frontier)\n",
|
" frontier = expand(frontier)\n",
|
||||||
@@ -642,12 +669,64 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"Additional tests:"
|
"# A Different Track\n",
|
||||||
|
"\n",
|
||||||
|
"Although the code here is not ready to handle large, arbitrary-shaped tracks, it can handle some small variations as is, and other variations with some tweaking. Here I define `track2` to be a track with a larger central circle, and a circular ring of valid points around the circle rather than a full rectangular grid:"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 18,
|
"execution_count": 19,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"[[6, 6, 3, 1, 1, 2, 4, 7, 4, 7, 7, 8, 9, 9, 9, 9, 5, 3, 6, 3, 3],\n",
|
||||||
|
" [6, 6, 3, 1, 1, 2, 4, 7, 4, 7, 7, 8, 9, 9, 9, 9, 5, 3, 3, 6, 6],\n",
|
||||||
|
" [6, 6, 3, 1, 1, 2, 4, 7, 4, 7, 7, 8, 9, 9, 9, 9, 5, 3, 3, 6, 3]]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 19,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"image/png": "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 truncated
|
||||||
|
"text/plain": [
|
||||||
|
"<Figure size 432x288 with 1 Axes>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "display_data"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"radius2 = 10\n",
|
||||||
|
"track2 = Track({Point(x, y) for x in range(-14, 15) for y in range(-14, 15) \n",
|
||||||
|
" if radius2 <= abs(Point(x, y)) < 13}, \n",
|
||||||
|
" start=Point(0, -12), \n",
|
||||||
|
" finish=[Point(0, -radius2), Point(0, -12)], \n",
|
||||||
|
" radius=radius2)\n",
|
||||||
|
"\n",
|
||||||
|
"solutions2 = search(track2)\n",
|
||||||
|
" \n",
|
||||||
|
"plot(solutions2, track=track2)\n",
|
||||||
|
"\n",
|
||||||
|
"[digits_from_path(path) for path in solutions2]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# Additional tests:"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 20,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
@@ -656,21 +735,21 @@
|
|||||||
"'tests pass'"
|
"'tests pass'"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 18,
|
"execution_count": 20,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "execute_result"
|
"output_type": "execute_result"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"path1 = [start, Point(1, -4)] # First move\n",
|
"path1 = [track.start, Point(1, -4)] # First move\n",
|
||||||
"\n",
|
"\n",
|
||||||
"assert start == Point(0, -5) == 0-5j\n",
|
"assert track.start == Point(0, -5) == 0-5j\n",
|
||||||
"assert X(start) == 0 and Y(start) == -5 \n",
|
"assert X(track.start) == 0 and Y(track.start) == -5 \n",
|
||||||
"assert velocity([start]) == 0\n",
|
"assert velocity([track.start]) == 0\n",
|
||||||
"assert velocity(path1) == Vector(1, 1)\n",
|
"assert velocity(path1) == Vector(1, 1)\n",
|
||||||
"assert XY(path1) == ([0, 1], [-5, -4])\n",
|
"assert XY(path1) == ([0, 1], [-5, -4])\n",
|
||||||
"\n",
|
"\n",
|
||||||
"assert quadrant(start) == 4\n",
|
"assert quadrant(track.start) == 4\n",
|
||||||
"assert quadrant(Point(1, 2)) == 1\n",
|
"assert quadrant(Point(1, 2)) == 1\n",
|
||||||
"assert quadrant(Point(-5, 4)) == 2\n",
|
"assert quadrant(Point(-5, 4)) == 2\n",
|
||||||
"assert quadrant(Point(-1, -1)) == 3\n",
|
"assert quadrant(Point(-1, -1)) == 3\n",
|
||||||
@@ -680,29 +759,21 @@
|
|||||||
" 0j,1+2j,2+4j,3+6j,4+8j,5+10j,6+12j,7+14j,8+16j,9+18j,10+20j]\n",
|
" 0j,1+2j,2+4j,3+6j,4+8j,5+10j,6+12j,7+14j,8+16j,9+18j,10+20j]\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# There are 9 moves from the start point\n",
|
"# There are 9 moves from the start point\n",
|
||||||
"assert all_moves([start]) == {-1-4j, -1-5j, -1-6j, \n",
|
"assert all_moves([track.start]) == {\n",
|
||||||
" 0-4j, 0-5j, 0-6j, \n",
|
" -1-4j, -1-5j, -1-6j, \n",
|
||||||
" 1-4j, 1-5j, 1-6j}\n",
|
" 0-4j, 0-5j, 0-6j, \n",
|
||||||
|
" 1-4j, 1-5j, 1-6j}\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# But 3 moves are backwards and 1 stays in the same place; those are disallowed\n",
|
"# But 3 moves are backwards and 1 stays in the same place; those are disallowed\n",
|
||||||
"assert circular_track_moves([start]) == {0-6j, 1-6j, 1-5j, 0-4j, 1-4j}\n",
|
"assert circular_track_moves([track.start]) == {0-6j, 1-6j, 1-5j, 0-4j, 1-4j}\n",
|
||||||
"assert expand({(start, zero): [start]}) == {\n",
|
"assert expand({(track.start, zero): [track.start]}) == {\n",
|
||||||
" (0-6j, 0-1j): [0-5j, 0-6j],\n",
|
" (0-6j, 0-1j): [0-5j, 0-6j],\n",
|
||||||
" (1-6j, 1-1j): [0-5j, 1-6j],\n",
|
" (1-6j, 1-1j): [0-5j, 1-6j],\n",
|
||||||
" (1-5j, 1+0j): [0-5j, 1-5j],\n",
|
" (1-5j, 1+0j): [0-5j, 1-5j],\n",
|
||||||
" (0-4j, 0+1j): [0-5j, 0-4j],\n",
|
" (0-4j, 0+1j): [0-5j, 0-4j],\n",
|
||||||
" (1-4j, 1+1j): [0-5j, 1-4j]}\n",
|
" (1-4j, 1+1j): [0-5j, 1-4j]}\n",
|
||||||
"\n",
|
"\n",
|
||||||
"def is_valid_path(path, legal_moves=circular_track_moves) -> bool:\n",
|
|
||||||
" \"\"\"A valid path is complete, starts at start, stays on track, and has all legal moves.\"\"\"\n",
|
|
||||||
" return (is_complete(path) \n",
|
|
||||||
" and path[0] == start\n",
|
|
||||||
" and set(path) <= track\n",
|
|
||||||
" and all(path[i] in legal_moves(path[:i]) \n",
|
|
||||||
" for i in range(1, len(path))))\n",
|
|
||||||
"\n",
|
|
||||||
"for path in solutions:\n",
|
"for path in solutions:\n",
|
||||||
" assert is_valid_path(path)\n",
|
|
||||||
" digits = digits_from_path(path)\n",
|
" digits = digits_from_path(path)\n",
|
||||||
" assert path == path_from_digits(digits_from_path(path)) # Inverses\n",
|
" assert path == path_from_digits(digits_from_path(path)) # Inverses\n",
|
||||||
" assert digits == digits_from_path(path_from_digits(digits)) # Inverses\n",
|
" assert digits == digits_from_path(path_from_digits(digits)) # Inverses\n",
|
||||||
|
|||||||
Reference in new issue
Block a user