1use std::collections::BTreeMap;
20
21use crate::util::integer::*;
22use crate::util::parse::*;
23use crate::util::point::*;
24
25type Input = (i32, i32);
26
27struct Line {
28 start: Point,
29 end: Point,
30 distance: i32,
31}
32
33pub fn parse(input: &str) -> Input {
34 let (first, second) = input.split_once('\n').unwrap();
35
36 let mut start = ORIGIN;
38 let mut distance = 0;
39 let mut vertical = BTreeMap::new();
40 let mut horizontal = BTreeMap::new();
41
42 for (direction, amount) in steps(first) {
43 let delta = Point::from(direction);
44 let end = start + delta * amount;
45 let line = Line { start, end, distance };
46
47 if start.x == end.x {
48 vertical.insert(start.x, line);
49 } else {
50 horizontal.insert(start.y, line);
51 }
52
53 start = end;
54 distance += amount;
55 }
56
57 let mut start = ORIGIN;
59 let mut distance = 0;
60 let mut manhattan = i32::MAX;
61 let mut delay = i32::MAX;
62
63 for (direction, amount) in steps(second) {
64 let delta = Point::from(direction);
65 let end = start + delta * amount;
66
67 let mut update = |line: &Line, candidate: Point| {
69 if candidate.manhattan(line.start) < line.end.manhattan(line.start)
70 && signum(candidate, line.start) == signum(line.end, line.start)
71 && candidate.manhattan(ORIGIN) > 0
72 {
73 manhattan = manhattan.min(candidate.manhattan(ORIGIN));
74 delay = delay.min(
75 distance
76 + candidate.manhattan(start)
77 + line.distance
78 + candidate.manhattan(line.start),
79 );
80 }
81 };
82
83 if start.x == end.x {
85 let (lo, hi) = start.y.minmax(end.y);
86 for (&y, line) in horizontal.range(lo..=hi) {
87 update(line, Point::new(start.x, y));
88 }
89 } else {
90 let (lo, hi) = start.x.minmax(end.x);
91 for (&x, line) in vertical.range(lo..=hi) {
92 update(line, Point::new(x, start.y));
93 }
94 }
95
96 start = end;
97 distance += amount;
98 }
99
100 (manhattan, delay)
101}
102
103pub fn part1(input: &Input) -> i32 {
104 input.0
105}
106
107pub fn part2(input: &Input) -> i32 {
108 input.1
109}
110
111fn steps(wire: &str) -> impl Iterator<Item = (u8, i32)> {
113 let directions = wire.bytes().filter(u8::is_ascii_alphabetic);
114 let amounts = wire.iter_signed::<i32>();
115 directions.zip(amounts)
116}
117
118fn signum(a: Point, b: Point) -> Point {
119 Point::new((a.x - b.x).signum(), (a.y - b.y).signum())
120}