diff options
Diffstat (limited to 'day12')
| -rw-r--r-- | day12/__init__.py | 65 |
1 files changed, 10 insertions, 55 deletions
diff --git a/day12/__init__.py b/day12/__init__.py index 69f9458..ef03ee0 100644 --- a/day12/__init__.py +++ b/day12/__init__.py | |||
| @@ -5,6 +5,7 @@ from math import inf, sqrt, floor | |||
| 5 | from typing import List, Tuple, Iterator, Callable, Any | 5 | from typing import List, Tuple, Iterator, Callable, Any |
| 6 | 6 | ||
| 7 | from aoc import BaseAssignment | 7 | from aoc import BaseAssignment |
| 8 | from aoc.mixins import AStarMixin | ||
| 8 | 9 | ||
| 9 | 10 | ||
| 10 | class Map: | 11 | class Map: |
| @@ -57,7 +58,7 @@ class Map: | |||
| 57 | return elevation - 96 | 58 | return elevation - 96 |
| 58 | 59 | ||
| 59 | 60 | ||
| 60 | class Assignment(BaseAssignment, ABC): | 61 | class Assignment(BaseAssignment, AStarMixin[Tuple[int, int]], ABC): |
| 61 | def distance(self, map: Map, a: Tuple[int, int], b: Tuple[int, int]): | 62 | def distance(self, map: Map, a: Tuple[int, int], b: Tuple[int, int]): |
| 62 | distance = map.elevation(*b) - map.elevation(*a) | 63 | distance = map.elevation(*b) - map.elevation(*a) |
| 63 | 64 | ||
| @@ -66,67 +67,22 @@ class Assignment(BaseAssignment, ABC): | |||
| 66 | 67 | ||
| 67 | return 1 | 68 | return 1 |
| 68 | 69 | ||
| 70 | |||
| 71 | class AssignmentOne(Assignment): | ||
| 72 | example_result = 31 | ||
| 73 | |||
| 69 | def heuristic(self, map: Map, a: Tuple[int, int]): | 74 | def heuristic(self, map: Map, a: Tuple[int, int]): |
| 70 | dx = abs(map.start[0] - a[0]) | 75 | dx = abs(map.start[0] - a[0]) |
| 71 | dy = abs(map.start[1] - a[1]) | 76 | dy = abs(map.start[1] - a[1]) |
| 72 | return floor(sqrt(dx**2 + dy**2)) | 77 | return floor(sqrt(dx**2 + dy**2)) |
| 73 | 78 | ||
| 74 | def a_star( | ||
| 75 | self, | ||
| 76 | map: Map, | ||
| 77 | start: Tuple[int, int], | ||
| 78 | end: Callable[[int, int], bool], | ||
| 79 | distance: Callable[[Tuple[int, int], Tuple[int, int]], int], | ||
| 80 | heuristic: Callable[[Tuple[int, int]], int], | ||
| 81 | ) -> List[Tuple[int, int]]: | ||
| 82 | open = {start} | ||
| 83 | came_from = {} | ||
| 84 | |||
| 85 | g_scores = {start: 0} | ||
| 86 | |||
| 87 | f_scores = {start: heuristic(start)} | ||
| 88 | |||
| 89 | while True: | ||
| 90 | try: | ||
| 91 | current = sorted( | ||
| 92 | [item for item in open], key=lambda item: f_scores.get(item, inf) | ||
| 93 | )[0] | ||
| 94 | except IndexError: | ||
| 95 | raise RuntimeError("No path found") | ||
| 96 | |||
| 97 | if end(*current): | ||
| 98 | path = [current] | ||
| 99 | |||
| 100 | while current in came_from: | ||
| 101 | current = came_from[current] | ||
| 102 | path = [current, *path] | ||
| 103 | |||
| 104 | return path | ||
| 105 | |||
| 106 | open.remove(current) | ||
| 107 | |||
| 108 | for neighbour in map.neighbours(*current): | ||
| 109 | g_score = g_scores.get(current, inf) + distance(current, neighbour) | ||
| 110 | |||
| 111 | if g_score < g_scores.get(neighbour, inf): | ||
| 112 | came_from[neighbour] = current | ||
| 113 | g_scores[neighbour] = g_score | ||
| 114 | f_scores[neighbour] = g_score + heuristic(neighbour) | ||
| 115 | |||
| 116 | if neighbour not in open: | ||
| 117 | open.add(neighbour) | ||
| 118 | |||
| 119 | |||
| 120 | class AssignmentOne(Assignment): | ||
| 121 | example_result = 31 | ||
| 122 | |||
| 123 | def run(self, input: Iterator) -> Any: | 79 | def run(self, input: Iterator) -> Any: |
| 124 | map = Map(map=list(input)) | 80 | map = Map(map=list(input)) |
| 125 | 81 | ||
| 126 | path = self.a_star( | 82 | path = self.a_star( |
| 127 | map, | ||
| 128 | map.start, | 83 | map.start, |
| 129 | lambda x, y: (x, y) == map.end, | 84 | lambda coordinate, _: coordinate == map.end, |
| 85 | neighbours=lambda a: map.neighbours(*a), | ||
| 130 | distance=lambda a, b: self.distance(map, a, b), | 86 | distance=lambda a, b: self.distance(map, a, b), |
| 131 | heuristic=lambda a: self.heuristic(map, a), | 87 | heuristic=lambda a: self.heuristic(map, a), |
| 132 | ) | 88 | ) |
| @@ -141,11 +97,10 @@ class AssignmentTwo(Assignment): | |||
| 141 | map = Map(list(input)) | 97 | map = Map(list(input)) |
| 142 | 98 | ||
| 143 | path = self.a_star( | 99 | path = self.a_star( |
| 144 | map, | ||
| 145 | map.end, | 100 | map.end, |
| 146 | lambda x, y: map.elevation(x, y) == 1, | 101 | lambda coordinate, _: map.elevation(*coordinate) == 1, |
| 102 | neighbours=lambda a: map.neighbours(*a), | ||
| 147 | distance=lambda a, b: self.distance(map, b, a), | 103 | distance=lambda a, b: self.distance(map, b, a), |
| 148 | heuristic=lambda a: 1, | ||
| 149 | ) | 104 | ) |
| 150 | 105 | ||
| 151 | return len(path) - 1 | 106 | return len(path) - 1 |
