summaryrefslogtreecommitdiffstats
path: root/day12/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'day12/__init__.py')
-rw-r--r--day12/__init__.py65
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
5from typing import List, Tuple, Iterator, Callable, Any 5from typing import List, Tuple, Iterator, Callable, Any
6 6
7from aoc import BaseAssignment 7from aoc import BaseAssignment
8from aoc.mixins import AStarMixin
8 9
9 10
10class Map: 11class Map:
@@ -57,7 +58,7 @@ class Map:
57 return elevation - 96 58 return elevation - 96
58 59
59 60
60class Assignment(BaseAssignment, ABC): 61class 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
71class 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
120class 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