83 lines
1.9 KiB
Python
83 lines
1.9 KiB
Python
#!/usr/bin/env python3
|
|
|
|
up = lambda v: (v[0], v[1] + 1)
|
|
down = lambda v: (v[0], v[1] - 1)
|
|
left = lambda v: (v[0] - 1, v[1])
|
|
right = lambda v: (v[0] + 1, v[1])
|
|
|
|
moves = {'U' : up,
|
|
'D' : down,
|
|
'L' : left,
|
|
'R' : right
|
|
}
|
|
|
|
def memoize(f):
|
|
cache = dict()
|
|
def memf(path):
|
|
if path not in cache:
|
|
cache[path] = f(path)
|
|
return cache[path]
|
|
return memf
|
|
|
|
def iterate(f, x, N):
|
|
for i in range(N):
|
|
yield x
|
|
x = f(x)
|
|
|
|
@memoize
|
|
def lines(path):
|
|
pos = (0, 0)
|
|
r = []
|
|
for move in path.split(','):
|
|
dir, steps = move[0], int(move[1:])
|
|
line = list(iterate(moves[dir], pos, steps+1))
|
|
r.append(line)
|
|
pos = line[-1]
|
|
return r
|
|
|
|
def coordinates(path, corners=True):
|
|
return sum((line[1:len(line) - 1 + corners]
|
|
for line in lines(path)), [])
|
|
|
|
def crossings(*paths):
|
|
return set.intersection(*(set(coordinates(path, corners=False))
|
|
for path in paths))
|
|
|
|
def min_crossing(path1, path2, f):
|
|
return min(f(v) for v in crossings(path1, path2))
|
|
|
|
def preproc(puzzle_input):
|
|
return puzzle_input.split()
|
|
|
|
def partI(paths):
|
|
key = lambda v: abs(v[0]) + abs(v[1])
|
|
|
|
path1, path2 = paths
|
|
return min_crossing(path1, path2, key)
|
|
|
|
def partII(paths):
|
|
path1, path2 = paths
|
|
def key(c1, c2):
|
|
r = lambda v: c1.index(v) + c2.index(v) + 2
|
|
return r
|
|
|
|
def min_steps(p1, p2):
|
|
c1, c2 = coordinates(p1), coordinates(p2)
|
|
return min_crossing(p1, p2, key(c1, c2))
|
|
|
|
return min_steps(path1, path2)
|
|
|
|
from main import Tests
|
|
|
|
tests = Tests()
|
|
tests.add("R75,D30,R83,U83,L12,D49,R71,U7,L72\n" +
|
|
"U62,R66,U55,R34,D71,R55,D58,R83",
|
|
partI=159,
|
|
partII=610)
|
|
|
|
tests.add("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51\n" +
|
|
"U98,R91,D20,R16,D67,R40,U7,R15,U6,R7",
|
|
partI=135,
|
|
partII=410)
|
|
|