1use utils::parser::{ParseError, Parser};
2use utils::prelude::*;
3
4#[derive(Clone, Debug)]
6pub struct Day06 {
7 parents: Vec<u16>,
8 depths: Vec<u16>,
9 you: u16,
10 san: u16,
11}
12
13const RADIX: u16 = 36;
14const LABEL_SPACE: usize = (36 * 36 * 36) + (36 * 36) + (36);
15const LABEL_BYTE_LUT: [Option<u16>; 256] = {
16 let mut result = [None; 256];
17
18 let mut i = 0;
19 while i < 10 {
20 result[(b'0' as usize) + i] = Some(i as u16);
21 i += 1;
22 }
23
24 let mut i = 0;
25 while i < 26 {
26 result[(b'A' as usize) + i] = Some(i as u16 + 10);
27 i += 1;
28 }
29
30 result
31};
32
33const fn encode(label: [u8; 3]) -> u16 {
34 const fn encode_byte(b: u8) -> u16 {
35 LABEL_BYTE_LUT[b as usize].unwrap()
36 }
37 encode_byte(label[0]) * RADIX * RADIX + encode_byte(label[1]) * RADIX + encode_byte(label[2])
38}
39const COM: u16 = encode(*b"COM");
40const YOU: u16 = encode(*b"YOU");
41const SAN: u16 = encode(*b"SAN");
42
43impl Day06 {
44 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
45 let label_byte = parser::byte_lut(
46 &LABEL_BYTE_LUT,
47 ParseError::Custom("expected uppercase letter or digit"),
48 );
49 let label = parser::parse_tree!((a @ label_byte) =>> {
50 (b @ label_byte) =>> {
51 (c @ label_byte) => a * RADIX * RADIX + b * RADIX + c,
52 (parser::noop()) => (RADIX * RADIX * RADIX) + a * RADIX + b,
53 },
54 (parser::noop()) => (RADIX * RADIX * RADIX) + (RADIX * RADIX) + a,
55 });
56 let orbit = label
57 .then(label.with_prefix(")"))
58 .with_consumed()
59 .with_eol();
60
61 let mut label_to_dense = [u16::MAX; LABEL_SPACE];
62 let mut parents = Vec::with_capacity(2048);
63 for item in orbit.parse_iterator(input) {
64 let ((lhs, rhs), line): ((u16, u16), &[u8]) = item?;
65 if rhs == COM {
66 return Err(InputError::new(
67 input,
68 line,
69 "expected COM to have no parent",
70 ));
71 }
72 if lhs == rhs {
73 return Err(InputError::new(
74 input,
75 line,
76 "expected object to not orbit itself",
77 ));
78 }
79
80 let mut intern = |label| {
81 let dense_index = label_to_dense[usize::from(label)];
82 if dense_index != u16::MAX {
83 dense_index
84 } else {
85 let next = parents.len();
86 parents.push(u16::MAX);
87 label_to_dense[usize::from(label)] = next as u16;
88 next as u16
89 }
90 };
91 let lhs = intern(lhs);
92 let rhs = intern(rhs);
93
94 if parents[usize::from(rhs)] != u16::MAX {
95 return Err(InputError::new(
96 input,
97 line,
98 "expected each object to have one parent",
99 ));
100 }
101 parents[usize::from(rhs)] = lhs;
102 }
103
104 let com = label_to_dense[usize::from(COM)];
105 if com == u16::MAX {
106 return Err(InputError::new(input, 0, "expected COM object"));
107 }
108 let mut depths = vec![u16::MAX; parents.len()];
109 depths[usize::from(com)] = 0;
110
111 let mut stack = Vec::with_capacity(256);
112 for start in 0..parents.len() {
113 if depths[start] != u16::MAX {
114 continue;
115 }
116
117 let mut current = start;
118 while depths[current] == u16::MAX {
119 stack.push(current as u16);
120
121 if stack.len() > parents.len() {
122 return Err(InputError::new(input, 0, "expected acyclic orbit graph"));
123 }
124
125 let next = parents[current];
126 if next == u16::MAX {
127 return Err(InputError::new(
128 input,
129 0,
130 "expected all objects to connect to COM",
131 ));
132 }
133 current = usize::from(next);
134 }
135
136 let mut depth = depths[current];
137 while let Some(node) = stack.pop() {
138 depth += 1;
139 depths[usize::from(node)] = depth;
140 }
141 }
142
143 Ok(Self {
144 parents,
145 depths,
146 you: label_to_dense[usize::from(YOU)],
147 san: label_to_dense[usize::from(SAN)],
148 })
149 }
150
151 #[must_use]
152 pub fn part1(&self) -> u32 {
153 self.depths.iter().map(|&depth| u32::from(depth)).sum()
154 }
155
156 #[must_use]
157 pub fn part2(&self) -> u32 {
158 if self.you == u16::MAX || self.san == u16::MAX {
159 panic!("expected YOU and SAN objects");
160 }
161
162 let mut a = usize::from(self.parents[self.you as usize]);
163 let mut b = usize::from(self.parents[self.san as usize]);
164 let mut distance = 0;
165 while self.depths[a] > self.depths[b] {
166 a = usize::from(self.parents[a]);
167 distance += 1;
168 }
169 while self.depths[b] > self.depths[a] {
170 b = usize::from(self.parents[b]);
171 distance += 1;
172 }
173 while a != b {
174 a = usize::from(self.parents[a]);
175 b = usize::from(self.parents[b]);
176 distance += 2;
177 }
178
179 distance
180 }
181}
182
183examples!(Day06 -> (u32, u32) [
184 {
185 input: "COM)B\nB)C\nC)D\nD)E\nE)F\nB)G\nG)H\nD)I\nE)J\nJ)K\nK)L",
186 part1: 42,
187 },
188 {
189 input: "COM)B\nB)C\nC)D\nD)E\nE)F\nB)G\nG)H\nD)I\nE)J\nJ)K\nK)L\nK)YOU\nI)SAN",
190 part2: 4,
191 },
192]);