Skip to content

Commit

Permalink
Doc updates
Browse files Browse the repository at this point in the history
  • Loading branch information
derailed-dash committed Dec 18, 2024
1 parent 1f76c82 commit 7ad4906
Showing 1 changed file with 28 additions and 13 deletions.
41 changes: 28 additions & 13 deletions src/AoC_2024/Dazbo's_Advent_of_Code_2024.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 194,
"metadata": {
"id": "p5Ki_HvOJUWk",
"tags": []
Expand All @@ -85,7 +85,7 @@
"from collections import Counter, deque, defaultdict\n",
"from dataclasses import asdict, dataclass, field\n",
"from enum import Enum, auto\n",
"from functools import cache, reduce\n",
"from functools import cache, lru_cache, reduce\n",
"from itertools import combinations, count, cycle, permutations, product, groupby\n",
"from getpass import getpass\n",
"from numbers import Number\n",
Expand Down Expand Up @@ -6734,22 +6734,31 @@
"\n",
"**Simulate the first kilobyte (1024 bytes) falling onto your memory space. Afterward, what is the minimum number of steps needed to reach the exit?**\n",
"\n",
"Okay, so this seems like a fairly trivial matter of corrupting the locations of 1024 bytes in our list, and the doing a BFS through the resulting maze. (I've got a bad feeling about part 2!)\n",
"Okay, so this seems like a fairly trivial matter of corrupting the locations of 1024 bytes in our list, and then doing a BFS through the resulting maze. (I've got a bad feeling about part 2!)\n",
"\n",
"We could use BFS, but because we know where we need to get to, [A*](https://aoc.just2good.co.uk/python/shortest_paths#a-algorithm) might be a better approach.\n",
"\n",
"I've created a `MemorySpace` class, which extends my usual `Grid` class. It contains a `get_shortest_path()` method, which simply implements the A* algorithm. A* is nearly identical to [Dijkstra's Algorithm](https://aoc.just2good.co.uk/python/shortest_paths#dijkstras-algorithm), except that instead of simply popping from the queue the path with the least cost so far (which in this case would be the number of steps taken), we also include a _distance heuristic_, i.e. we add a factor that indicates how far we are away from the destination. So in my implementation, I've made the cost the combination of steps taken, and the Manhattan distance from the destination.\n",
"\n",
"We could use BFS, but because we know where we need to get to, [A*](https://aoc.just2good.co.uk/python/shortest_paths#dijkstras-algorithm) is probably a better approach."
"And that's it!"
]
},
{
"cell_type": "code",
"execution_count": 160,
"execution_count": 207,
"metadata": {},
"outputs": [],
"source": [
"class MemorySpace(Grid):\n",
" def __init__(self, grid_array):\n",
" super().__init__(grid_array)\n",
" \n",
" @lru_cache(maxsize=1000)\n",
" def _get_manhattan_distance_between(self, point_from: Point, point_to: Point):\n",
" return point_from.manhattan_distance_from(point_to)\n",
" \n",
" def a_star_path(self, start: Point, end: Point):\n",
" def get_shortest_path(self, start: Point, end: Point):\n",
" \"\"\" Determine shortest path from start to end, using A* \"\"\"\n",
" queue: tuple[int, int, Point] = []\n",
" backtrace = {}\n",
" backtrace[start] = None\n",
Expand All @@ -6768,15 +6777,15 @@
" for neighbour in valid_neighbours:\n",
" if neighbour not in backtrace:\n",
" backtrace[neighbour] = current_posn\n",
" cost = neighbour.manhattan_distance_from(end) + (step_count + 1)\n",
" cost = self._get_manhattan_distance_between(neighbour, end) + (step_count + 1)\n",
" heapq.heappush(queue, (cost, step_count+1, neighbour))\n",
" \n",
" raise ValueError(\"No solution found\")\n"
]
},
{
"cell_type": "code",
"execution_count": 161,
"execution_count": 208,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -6793,7 +6802,7 @@
" \n",
" logger.debug(f\"\\n{mem_space}\")\n",
" \n",
" step_count, backtrace = mem_space.a_star_path(Point(0,0), Point(grid_length-1, grid_length-1))\n",
" step_count, _ = mem_space.get_shortest_path(Point(0,0), Point(grid_length-1, grid_length-1))\n",
" return step_count"
]
},
Expand Down Expand Up @@ -6867,12 +6876,17 @@
"\n",
"We don't need to repeat the search for every byte dropped. We only need to try a new path if the last byte dropped has blocked our current best path. So we can skip the majority of bytes dropped.\n",
"\n",
"With this tweak, the solution now runs in under a minute. Still slow, but good enough!"
"With this tweak, the solution now runs in under a minute. Still slow, but good enough!\n",
"\n",
"Trying other quick optimisations:\n",
"\n",
"- I tried caching the Manhattan distance. But this made no noticeable improvement.\n",
"- Depending on how convoluted the path is, A* may hinder rather than help. So I've tried removing the Manhattan distance factor, and just running this as a Dijkstra. This also made no noticeable difference."
]
},
{
"cell_type": "code",
"execution_count": 167,
"execution_count": 212,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -6892,8 +6906,9 @@
" \n",
" mem_space.set_value_at_point(point, \"#\")\n",
" try:\n",
" _, backtrace = mem_space.a_star_path(Point(0,0), Point(grid_length-1, grid_length-1))\n",
" except ValueError:\n",
" _, backtrace = mem_space.get_shortest_path(Point(0,0), Point(grid_length-1, grid_length-1))\n",
" except ValueError as e:\n",
" logger.debug(e)\n",
" break # No solution - we've found the offending byte!\n",
" \n",
" return f\"{point.x},{point.y}\""
Expand Down

0 comments on commit 7ad4906

Please sign in to comment.