-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsolution_12.py
136 lines (120 loc) · 3.78 KB
/
solution_12.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from collections import deque
import numpy as np
from utils import utils
from tqdm import tqdm
INPUT_PATH = "inputs/input_12.txt"
HEIGHTS = {
"a": 1,
"b": 2,
"c": 3,
"d": 4,
"e": 5,
"f": 6,
"g": 7,
"h": 8,
"i": 9,
"j": 10,
"k": 11,
"l": 12,
"m": 13,
"n": 14,
"o": 15,
"p": 16,
"q": 17,
"r": 18,
"s": 19,
"t": 20,
"u": 21,
"v": 22,
"w": 23,
"x": 24,
"y": 25,
"z": 26,
"S": 0,
"E": 27,
}
class Node:
def __init__(self):
self.reachable_neighbours = None
self.symbol = None
def is_start(self):
if self.symbol == "S":
return True
else:
return False
def is_end(self):
if self.symbol == "E":
return True
else:
return False
class HeightMap:
def __init__(self):
self.raw_heights = None
self.height_map = {}
self.start_pos = None
self.end_pos = None
self.low_poss = []
def load_raw_heights(self, input_path=INPUT_PATH):
with open(input_path) as input_data:
self.raw_heights = input_data.read().splitlines()
def decode_raw_heights(self):
heights = np.array(
[np.array(list(map(lambda char: HEIGHTS[char], line)), dtype=int) for line in self.raw_heights]
)
shape = heights.shape
for j, row in enumerate(heights):
for i, height in enumerate(row):
correction = 0
if height == 0:
self.start_pos = (j, i)
correction = 1
elif height == 27:
self.end_pos = (j, i)
correction = -1
elif height == 1:
self.low_poss.append((j, i))
reachable_nodes = []
real_height = height + correction
if i - 1 >= 0 and heights[j, i - 1] <= real_height + 1:
reachable_nodes.append((j, i - 1))
if i + 1 < shape[1] and heights[j, i + 1] <= real_height + 1:
reachable_nodes.append((j, i + 1))
if j - 1 >= 0 and heights[j - 1, i] <= real_height + 1:
reachable_nodes.append((j - 1, i))
if j + 1 < shape[0] and heights[j + 1, i] <= real_height + 1:
reachable_nodes.append((j + 1, i))
self.height_map[(j, i)] = reachable_nodes
def find_shortest_path_distance(self, start_pos=None): # bfs
to_search = deque()
if start_pos == None:
to_search.append((0, self.start_pos))
else:
to_search.append((0, start_pos))
searched = []
while to_search:
distance, node = to_search.popleft()
if node not in searched:
if node == self.end_pos:
return distance
else:
for neighbour in self.height_map[node]:
to_search.append((distance + 1, neighbour))
searched.append(node)
return -1
def find_best_shortest_path(self): # yolo, can't bother reversing the search from top to bottom
shortest = float("inf")
for pos in tqdm(self.low_poss):
distance = self.find_shortest_path_distance(pos)
if distance > 0:
shortest = min(shortest, distance)
return shortest
def main():
terrain = HeightMap()
terrain.load_raw_heights()
terrain.decode_raw_heights()
distance = terrain.find_shortest_path_distance()
shortest_distance = terrain.find_best_shortest_path()
utils.write_answers_to_file(distance, shortest_distance, file_name="answer_12.txt")
print(distance, shortest_distance)
if __name__ == "__main__":
main()