#!/usr/bin/env python3 from functools import cache 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 iterate(f, x, N): for i in range(N): yield x x = f(x) @cache 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)