year2019/
day06.rs

1use utils::parser::{ParseError, Parser};
2use utils::prelude::*;
3
4/// Counting depths and distances in a tree.
5#[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]);