1use utils::geometry::{Direction, Vec2};
2use utils::prelude::*;
3
4#[derive(Clone, Debug)]
8pub struct Day03 {
9 part1: u32,
10 part2: u32,
11}
12
13#[derive(Clone, Debug)]
14struct Segment {
15 min: Vec2<i32>,
16 max: Vec2<i32>,
17 start: Vec2<i32>,
18 start_steps: u32,
19}
20
21impl Day03 {
22 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
23 let [(mut wire1h, mut wire1v), (wire2h, wire2v)] = parser::byte_map!(
24 b'U' => Direction::Up,
25 b'R' => Direction::Right,
26 b'D' => Direction::Down,
27 b'L' => Direction::Left
28 )
29 .then(parser::u16())
30 .repeat_fold(
31 b',',
32 1,
33 (Vec::new(), Vec::new(), Vec2::ORIGIN, 0u32),
34 |(mut horizontal, mut vertical, start, steps), (dir, dist)| {
35 let end = start + Vec2::from(dir) * dist as i32;
36 let segment = Segment {
37 min: start.component_min(end),
38 max: start.component_max(end),
39 start,
40 start_steps: steps,
41 };
42 if matches!(dir, Direction::Left | Direction::Right) {
43 horizontal.push(segment);
44 } else {
45 vertical.push(segment);
46 }
47 (horizontal, vertical, end, steps + dist as u32)
48 },
49 )
50 .map(|(horizontal, vertical, _, _)| (horizontal, vertical))
51 .repeat_n(parser::eol())
52 .parse_complete(input)?;
53
54 wire1h.sort_unstable_by_key(|s| s.min.y);
55 wire1v.sort_unstable_by_key(|s| s.min.x);
56
57 let (mut part1, mut part2) = (u32::MAX, u32::MAX);
58 let mut check = |horizontal: &Segment, vertical: &Segment| {
59 let intersection = Vec2::new(vertical.min.x, horizontal.min.y);
60 if intersection.x >= horizontal.min.x
61 && intersection.x <= horizontal.max.x
62 && intersection.y >= vertical.min.y
63 && intersection.y <= vertical.max.y
64 && intersection != Vec2::ORIGIN
65 {
66 part1 = part1.min(intersection.manhattan_distance());
67
68 let h_steps = horizontal.start_steps + horizontal.start.x.abs_diff(intersection.x);
69 let v_steps = vertical.start_steps + vertical.start.y.abs_diff(intersection.y);
70 part2 = part2.min(h_steps + v_steps);
71 }
72 };
73
74 for horizontal in wire2h.iter() {
75 for vertical in wire1v
76 .iter()
77 .skip(wire1v.partition_point(|s| s.min.x < horizontal.min.x))
78 .take_while(|s| s.min.x <= horizontal.max.x)
79 {
80 check(horizontal, vertical);
81 }
82 }
83 for vertical in wire2v.iter() {
84 for horizontal in wire1h
85 .iter()
86 .skip(wire1h.partition_point(|s| s.min.y < vertical.min.y))
87 .take_while(|s| s.min.y <= vertical.max.y)
88 {
89 check(horizontal, vertical);
90 }
91 }
92
93 Ok(Self { part1, part2 })
94 }
95
96 #[must_use]
97 pub fn part1(&self) -> u32 {
98 self.part1
99 }
100
101 #[must_use]
102 pub fn part2(&self) -> u32 {
103 self.part2
104 }
105}
106
107examples!(Day03 -> (u32, u32) [
108 {
109 input: "R8,U5,L5,D3\nU7,R6,D4,L4",
110 part1: 6,
111 part2: 30,
112 },
113 {
114 input: "R75,D30,R83,U83,L12,D49,R71,U7,L72\nU62,R66,U55,R34,D71,R55,D58,R83",
115 part1: 159,
116 part2: 610,
117 },
118 {
119 input: "R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51\nU98,R91,D20,R16,D67,R40,U7,R15,U6,R7",
120 part1: 135,
121 part2: 410,
122 },
123]);