mirror of
https://github.com/cupcakearmy/advent-of-code.git
synced 2024-12-21 23:56:30 +00:00
day 12
This commit is contained in:
parent
fff2267f21
commit
792d0ddc02
18
2022/12/README.md
Normal file
18
2022/12/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# 12
|
||||||
|
|
||||||
|
What a ride...
|
||||||
|
|
||||||
|
So first I tried without A-Star. Well that of course did not stand a chance against the actual dataset.
|
||||||
|
|
||||||
|
I think my a start implementation is flawed, of the heuristics are not optimal. It starts in a straight line, but ends up checking every field anyways. So it's more like a Dijkstra. But it works.
|
||||||
|
The difference between Manhattan and Euclidean distance as `h(x)` did not help two. Went form 6939 iterations to 6974.
|
||||||
|
|
||||||
|
The second part was quick. I reversed the search, starting from the end, removed the heuristic, so ti's now a classic Dijkstra, and instead of searching for a specific node, i stopped at the first encounter of an `a` or `0` elevation.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Solutions</summary>
|
||||||
|
<ol>
|
||||||
|
<li>497</li>
|
||||||
|
<li>492</li>
|
||||||
|
</ol>
|
||||||
|
</details>
|
139
2022/12/python/main.py
Normal file
139
2022/12/python/main.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
from copy import deepcopy
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from os.path import dirname, join
|
||||||
|
|
||||||
|
# Day 12
|
||||||
|
|
||||||
|
# Common
|
||||||
|
|
||||||
|
|
||||||
|
def read_input(filename):
|
||||||
|
data = join(dirname(__file__), '..', filename)
|
||||||
|
with open(data) as f:
|
||||||
|
return f.read().strip()
|
||||||
|
|
||||||
|
|
||||||
|
test = read_input('test.txt')
|
||||||
|
data = read_input('input.txt')
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(unsafe_hash=True)
|
||||||
|
class Point:
|
||||||
|
y: int
|
||||||
|
x: int
|
||||||
|
|
||||||
|
def __add__(self, other: 'Point') -> 'Point':
|
||||||
|
return Point(self.y+other.y, self.x+other.x)
|
||||||
|
|
||||||
|
def distance(self, other: 'Point') -> int:
|
||||||
|
return int(((self.y-other.y)**2 + (self.x-other.x)**2)**(1/2))
|
||||||
|
return abs(self.y - other.y) + abs(self.x - other.y)
|
||||||
|
|
||||||
|
|
||||||
|
class Map:
|
||||||
|
def __init__(self, grid: list[list[int]], start: Point, end: Point) -> None:
|
||||||
|
self.start = start
|
||||||
|
self.end = end
|
||||||
|
self.grid = grid
|
||||||
|
|
||||||
|
self._max_y = len(self.grid)
|
||||||
|
self._max_x = len(self.grid[0])
|
||||||
|
self._step: tuple[Point, ...] = (Point(0, 1), Point(0, -1), Point(1, 0), Point(-1, 0))
|
||||||
|
|
||||||
|
self._display = [
|
||||||
|
[chr(c + ord('a')) for c in y]
|
||||||
|
for y in self.grid
|
||||||
|
]
|
||||||
|
|
||||||
|
def display_points(self, points: list[Point]):
|
||||||
|
display = deepcopy(self._display)
|
||||||
|
for p in points:
|
||||||
|
display[p.y][p.x] = '#'
|
||||||
|
print('\n'+'\n'.join([''.join(y) for y in display]))
|
||||||
|
|
||||||
|
def get_neighbors(self, point: Point):
|
||||||
|
points: list[Point] = []
|
||||||
|
for step in self._step:
|
||||||
|
delta = point + step
|
||||||
|
if delta.y > -1 and delta.y < self._max_y and delta.x > -1 and delta.x < self._max_x:
|
||||||
|
points.append(delta)
|
||||||
|
return points
|
||||||
|
|
||||||
|
def get_height(self, point: Point) -> int:
|
||||||
|
return self.grid[point.y][point.x]
|
||||||
|
|
||||||
|
def a_star(self, two: bool = False) -> int:
|
||||||
|
start = self.end if two else self.start
|
||||||
|
end = self.end
|
||||||
|
visited: list[Point] = [] # Our graph is not directed, so we need to keep track of already visited nodes
|
||||||
|
came_from: dict[Point, Point] = {} # To reconstruct the path once done
|
||||||
|
g: dict[Point, int] = {start: 0} # G Value for each node
|
||||||
|
h: dict[Point, int] = {}
|
||||||
|
to_consider: list[tuple[Point, float]] = [(start, g[start] + 0 if two else start.distance(self.end))]
|
||||||
|
|
||||||
|
# i = 0
|
||||||
|
while True:
|
||||||
|
to_consider = sorted(to_consider, key=lambda x: x[1])
|
||||||
|
current, f = to_consider.pop(0)
|
||||||
|
|
||||||
|
current_height = self.get_height(current)
|
||||||
|
if two:
|
||||||
|
if current_height == 0:
|
||||||
|
end = current
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if current == end:
|
||||||
|
break
|
||||||
|
|
||||||
|
visited.append(current)
|
||||||
|
neighbours = self.get_neighbors(current)
|
||||||
|
for neighbour in neighbours:
|
||||||
|
delta = self.get_height(neighbour) - current_height
|
||||||
|
invalid = delta < -1 if two else delta > 1
|
||||||
|
if neighbour in visited or invalid:
|
||||||
|
continue
|
||||||
|
tmp_g = g[current] + 1 # We only have simple 1 step costs in our graph
|
||||||
|
if neighbour not in g or tmp_g < g[neighbour]:
|
||||||
|
came_from[neighbour] = current
|
||||||
|
g[neighbour] = tmp_g
|
||||||
|
if neighbour not in h:
|
||||||
|
h[neighbour] = 0 if two else neighbour.distance(self.end)
|
||||||
|
tmp_h = h[neighbour]
|
||||||
|
tmp_f = tmp_g + tmp_h
|
||||||
|
to_consider.append((neighbour, tmp_f))
|
||||||
|
|
||||||
|
l: list[Point] = [end]
|
||||||
|
while True:
|
||||||
|
current = l[-1]
|
||||||
|
if current not in came_from:
|
||||||
|
self.display_points(l)
|
||||||
|
return len(l) - 1
|
||||||
|
prev = came_from[current]
|
||||||
|
l.append(prev)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse(data: str) -> 'Map':
|
||||||
|
offset = ord('a')
|
||||||
|
start: Point
|
||||||
|
end: Point
|
||||||
|
grid: list[list[int]] = []
|
||||||
|
for y, line in enumerate(data.splitlines()):
|
||||||
|
grid.append([])
|
||||||
|
for x, char in enumerate(line):
|
||||||
|
if char == 'S':
|
||||||
|
start = Point(y, x)
|
||||||
|
char = 'a'
|
||||||
|
if char == 'E':
|
||||||
|
end = Point(y, x)
|
||||||
|
char = 'z'
|
||||||
|
height = ord(char) - offset
|
||||||
|
grid[y].append(height)
|
||||||
|
return Map(grid, start, end)
|
||||||
|
|
||||||
|
|
||||||
|
# Running
|
||||||
|
m = Map.parse(data)
|
||||||
|
print(m.a_star(two=True))
|
Loading…
Reference in New Issue
Block a user