Skip to content

Commit 2cf5b7c

Browse files
committed
almost missed it :)
1 parent 1164fd7 commit 2cf5b7c

File tree

3 files changed

+744
-0
lines changed

3 files changed

+744
-0
lines changed

day20/d20-1.py

+342
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
# --- Day 20: Race Condition ---
2+
3+
# The Historians are quite pixelated again. This time, a massive,
4+
# black building looms over you - you're right outside the CPU!
5+
6+
# While The Historians get to work, a nearby program sees that you're
7+
# idle and challenges you to a race. Apparently, you've arrived just
8+
# in time for the frequently-held race condition festival!
9+
10+
# The race takes place on a particularly long and twisting code path;
11+
# programs compete to see who can finish in the fewest picoseconds.
12+
# The winner even gets their very own mutex!
13+
14+
# They hand you a map of the racetrack (your puzzle input). For example:
15+
16+
# ###############
17+
# #...#...#.....#
18+
# #.#.#.#.#.###.#
19+
# #S#...#.#.#...#
20+
# #######.#.#.###
21+
# #######.#.#...#
22+
# #######.#.###.#
23+
# ###..E#...#...#
24+
# ###.#######.###
25+
# #...###...#...#
26+
# #.#####.#.###.#
27+
# #.#...#.#.#...#
28+
# #.#.#.#.#.#.###
29+
# #...#...#...###
30+
# ###############
31+
32+
# The map consists of track (.) - including the start (S) and end (E)
33+
# positions (both of which also count as track) - and walls (#).
34+
35+
# When a program runs through the racetrack, it starts at the start
36+
# position. Then, it is allowed to move up, down, left, or right;
37+
# each such move takes 1 picosecond. The goal is to reach the end
38+
# position as quickly as possible. In this example racetrack, the
39+
# fastest time is 84 picoseconds.
40+
41+
# Because there is only a single path from the start to the end and the
42+
# programs all go the same speed, the races used to be pretty boring.
43+
# To make things more interesting, they introduced a new rule to the
44+
# races: programs are allowed to cheat.
45+
46+
# The rules for cheating are very strict. Exactly once during a race,
47+
# a program may disable collision for up to 2 picoseconds. This allows
48+
# the program to pass through walls as if they were regular track. At
49+
# the end of the cheat, the program must be back on normal track again;
50+
# otherwise, it will receive a segmentation fault and get disqualified.
51+
52+
# So, a program could complete the course in 72 picoseconds (saving 12
53+
# picoseconds) by cheating for the two moves marked 1 and 2:
54+
55+
# ###############
56+
# #...#...12....#
57+
# #.#.#.#.#.###.#
58+
# #S#...#.#.#...#
59+
# #######.#.#.###
60+
# #######.#.#...#
61+
# #######.#.###.#
62+
# ###..E#...#...#
63+
# ###.#######.###
64+
# #...###...#...#
65+
# #.#####.#.###.#
66+
# #.#...#.#.#...#
67+
# #.#.#.#.#.#.###
68+
# #...#...#...###
69+
# ###############
70+
71+
# Or, a program could complete the course in 64 picoseconds (saving
72+
# 20 picoseconds) by cheating for the two moves marked 1 and 2:
73+
74+
# ###############
75+
# #...#...#.....#
76+
# #.#.#.#.#.###.#
77+
# #S#...#.#.#...#
78+
# #######.#.#.###
79+
# #######.#.#...#
80+
# #######.#.###.#
81+
# ###..E#...12..#
82+
# ###.#######.###
83+
# #...###...#...#
84+
# #.#####.#.###.#
85+
# #.#...#.#.#...#
86+
# #.#.#.#.#.#.###
87+
# #...#...#...###
88+
# ###############
89+
90+
# This cheat saves 38 picoseconds:
91+
92+
# ###############
93+
# #...#...#.....#
94+
# #.#.#.#.#.###.#
95+
# #S#...#.#.#...#
96+
# #######.#.#.###
97+
# #######.#.#...#
98+
# #######.#.###.#
99+
# ###..E#...#...#
100+
# ###.####1##.###
101+
# #...###.2.#...#
102+
# #.#####.#.###.#
103+
# #.#...#.#.#...#
104+
# #.#.#.#.#.#.###
105+
# #...#...#...###
106+
# ###############
107+
108+
# This cheat saves 64 picoseconds and takes the program
109+
# directly to the end:
110+
111+
# ###############
112+
# #...#...#.....#
113+
# #.#.#.#.#.###.#
114+
# #S#...#.#.#...#
115+
# #######.#.#.###
116+
# #######.#.#...#
117+
# #######.#.###.#
118+
# ###..21...#...#
119+
# ###.#######.###
120+
# #...###...#...#
121+
# #.#####.#.###.#
122+
# #.#...#.#.#...#
123+
# #.#.#.#.#.#.###
124+
# #...#...#...###
125+
# ###############
126+
127+
# Each cheat has a distinct start position (the position where the cheat
128+
# is activated, just before the first move that is allowed to go through
129+
# walls) and end position; cheats are uniquely identified by their start
130+
# position and end position.
131+
132+
# In this example, the total number of cheats (grouped by the amount of
133+
# time they save) are as follows:
134+
135+
# There are 14 cheats that save 2 picoseconds.
136+
# There are 14 cheats that save 4 picoseconds.
137+
# There are 2 cheats that save 6 picoseconds.
138+
# There are 4 cheats that save 8 picoseconds.
139+
# There are 2 cheats that save 10 picoseconds.
140+
# There are 3 cheats that save 12 picoseconds.
141+
# There is one cheat that saves 20 picoseconds.
142+
# There is one cheat that saves 36 picoseconds.
143+
# There is one cheat that saves 38 picoseconds.
144+
# There is one cheat that saves 40 picoseconds.
145+
# There is one cheat that saves 64 picoseconds.
146+
147+
# You aren't sure what the conditions of the racetrack will be like, so
148+
# to give yourself as many options as possible, you'll need a list of
149+
# the best cheats. How many cheats would save you at least 100 picoseconds?
150+
151+
import numpy as np
152+
from tqdm import tqdm
153+
from multiprocessing import Pool
154+
from time import time
155+
from numba import jit
156+
from collections import defaultdict
157+
from math import ceil, floor, prod
158+
import sys
159+
from heapq import heapify, heappush, heappop
160+
161+
np.set_printoptions(threshold=np.inf)
162+
np.set_printoptions(linewidth=np.inf)
163+
164+
165+
def print_mat(m):
166+
np.set_printoptions(threshold=np.inf)
167+
np.set_printoptions(linewidth=np.inf)
168+
print(np.array2string(m, separator='', formatter={'str_kind':
169+
lambda x: x}))
170+
171+
172+
def conv_2d(arr, conv_f):
173+
return [[conv_f(a) for a in line] for line in arr]
174+
175+
176+
def flatten(xss):
177+
return [x for xs in xss for x in xs]
178+
179+
180+
def get_neighb(pos, shape):
181+
h, w = shape
182+
i, j = pos
183+
184+
ns = []
185+
if i > 0:
186+
ns.append((i - 1, j))
187+
if i < h - 1:
188+
ns.append((i + 1, j))
189+
if j > 0:
190+
ns.append((i, j - 1))
191+
if j < w - 1:
192+
ns.append((i, j + 1))
193+
194+
return ns
195+
196+
197+
def dktr2D(visited_dist, start_pos, end_pos=None, blockage=-1):
198+
# dikstra all paths algorithm for a 2D mat 'visited_dist'
199+
# 'visited_dist' have its path blocked by a blocking value
200+
# which cannot be traversed.
201+
# It is assumed that 'visited_dist' have all unvisited values
202+
# as max_value and its start position is 0.
203+
# If end_pos is not none, it will stop when reached
204+
205+
# max_dist = sys.maxsize
206+
shape = visited_dist.shape
207+
# visited_dist = np.full(shape, max_dist)
208+
# visited_dist[start_pos] = 0
209+
unvisited_queue = []
210+
heappush(unvisited_queue, (visited_dist[start_pos], start_pos))
211+
212+
# Try to find paths while there are options
213+
while len(unvisited_queue) > 0:
214+
dist, pos = heappop(unvisited_queue)
215+
216+
ns = get_neighb(pos, shape)
217+
for n in ns:
218+
new_dist = dist + 1
219+
if visited_dist[n] != blockage and new_dist < visited_dist[n]:
220+
visited_dist[n] = new_dist
221+
heappush(unvisited_queue, (new_dist, n))
222+
223+
224+
def find_path_from_visited(visited_dist, start_pos, end_pos, blockage=-1):
225+
pos = end_pos
226+
path = [end_pos]
227+
shape = visited_dist.shape
228+
while pos != start_pos:
229+
best_dist = sys.maxsize
230+
best_next = None
231+
for n in get_neighb(pos, shape):
232+
if visited_dist[n] != blockage and visited_dist[n] < best_dist:
233+
best_dist = visited_dist[n]
234+
best_next = n
235+
236+
if best_next == None:
237+
return []
238+
else:
239+
path.append(best_next)
240+
pos = best_next
241+
242+
return path
243+
244+
245+
def set_path(m, path, path_val=99):
246+
m2 = m.copy()
247+
h, w = m.shape
248+
for p in path:
249+
m2[p] = path_val
250+
for i in range(h):
251+
for j in range(w):
252+
if m2[i, j] > sys.maxsize / 100:
253+
m2[i, j] = -10
254+
return m2
255+
256+
257+
def main():
258+
inpt = []
259+
with open('input.txt', 'r') as f_in:
260+
inpt = f_in.readlines()
261+
262+
# Remove \n
263+
inpt = [i[:-1] for i in inpt]
264+
265+
racetrack = np.array([list(i) for i in inpt])
266+
h, w = racetrack.shape
267+
268+
for i in range(h):
269+
for j in range(w):
270+
if racetrack[i, j] == 'S':
271+
start_pos = (i, j)
272+
if racetrack[i, j] == 'E':
273+
end_pos = (i, j)
274+
275+
wall_char = '#'
276+
wall = -1
277+
max_dist = sys.maxsize
278+
279+
search_space = np.full((h, w), max_dist)
280+
search_space[start_pos] = 0
281+
282+
for i in range(h):
283+
for j in range(w):
284+
if racetrack[i, j] == wall_char:
285+
search_space[i, j] = wall
286+
287+
dktr2D(search_space, start_pos, end_pos=None, blockage=wall)
288+
new_path = find_path_from_visited(search_space, start_pos, end_pos)
289+
290+
path_val = 99
291+
path_space = set_path(search_space, new_path, path_val)
292+
print(path_space)
293+
print(new_path)
294+
print(len(new_path) - 1)
295+
296+
# Key: (i,j), value: global improvement
297+
cheat_list = dict()
298+
299+
def is_horz_cheat(path_space, i, j):
300+
return path_space[i, j] == wall and path_space[
301+
i, j - 1] == path_val and path_space[i, j + 1] == path_val
302+
303+
def is_vert_cheat(path_space, i, j):
304+
return path_space[i, j] == wall and path_space[
305+
i - 1, j] == path_val and path_space[i + 1, j] == path_val
306+
307+
# Attempt to find a -1,99,-1 pattern, i.e., a wall
308+
# Return the one with the highest diff
309+
cheat_cost = 2
310+
for i in range(1, h - 1):
311+
for j in range(1, w - 1):
312+
can_cheat = False
313+
314+
if is_horz_cheat(path_space, i, j):
315+
if search_space[i, j - 1] < search_space[i, j + 1]:
316+
cheat_start_pos = (i, j - 1)
317+
cheat_end_pos = (i, j + 1)
318+
else:
319+
cheat_start_pos = (i, j + 1)
320+
cheat_end_pos = (i, j - 1)
321+
can_cheat = True
322+
323+
if is_vert_cheat(path_space, i, j):
324+
if search_space[i - 1, j] < search_space[i + 1, j]:
325+
cheat_start_pos = (i - 1, j)
326+
cheat_end_pos = (i + 1, j)
327+
else:
328+
cheat_start_pos = (i + 1, j)
329+
cheat_end_pos = (i - 1, j)
330+
can_cheat = True
331+
332+
if can_cheat:
333+
improv = search_space[cheat_end_pos] - (
334+
cheat_cost + search_space[cheat_start_pos])
335+
cheat_list[i, j] = improv
336+
337+
print(cheat_list)
338+
print(sum(np.array(list(cheat_list.values()))>=100))
339+
340+
341+
if __name__ == '__main__':
342+
main()

0 commit comments

Comments
 (0)