1use utils::prelude::*;
2use utils::slice::merge_sorted_deduped_in_place;
3
4#[derive(Clone, Debug)]
6pub struct Day20 {
7 part1: u16,
8 part2: u16,
9}
10
11const WIDTH: usize = 109;
12
13impl Day20 {
14 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
15 if input.as_bytes().first() != Some(&b'^') {
16 return Err(InputError::new(input, 0, "expected '^'"));
17 }
18 if input.as_bytes().last() != Some(&b'$') {
19 return Err(InputError::new(input, input.len() - 1, "expected '$'"));
20 }
21
22 let mut grid = [0u16; WIDTH * WIDTH];
23 let mut positions = vec![(WIDTH * WIDTH) / 2];
24 let (mut start, mut end) = (Vec::new(), Vec::new());
25 let mut stack = Vec::with_capacity(256);
26
27 for (i, &b) in input.as_bytes()[..input.len() - 1]
28 .iter()
29 .enumerate()
30 .skip(1)
31 {
32 let dir = match b {
33 b'N' => -(WIDTH as isize),
34 b'E' => 1,
35 b'S' => WIDTH as isize,
36 b'W' => -1,
37 b'(' => {
38 stack.push((std::mem::take(&mut start), std::mem::take(&mut end)));
39 start.clone_from(&positions);
40
41 continue;
42 }
43 b'|' => {
44 if stack.is_empty() {
45 return Err(InputError::new(input, i, "unexpected '|'"));
46 }
47
48 merge_sorted_deduped_in_place(&mut end, &positions);
49 positions.clone_from(&start);
50
51 continue;
52 }
53 b')' => {
54 merge_sorted_deduped_in_place(&mut positions, &end);
55
56 let Some(entry) = stack.pop() else {
57 return Err(InputError::new(input, i, "unexpected ')'"));
58 };
59 (start, end) = entry;
60
61 continue;
62 }
63 _ => {
64 return Err(InputError::new(
65 input,
66 i,
67 "expected 'N', 'E', 'S', 'W', '(', '|' or ')'",
68 ));
69 }
70 };
71
72 for p in positions.iter_mut() {
73 let distance = grid[*p];
74 *p = p.wrapping_add_signed(dir);
75 if grid[*p] == 0 || distance + 1 < grid[*p] {
76 grid[*p] = distance + 1;
77 }
78 }
79 }
80
81 if !stack.is_empty() {
82 return Err(InputError::new(input, input.len() - 1, "expected ')'"));
83 }
84
85 Ok(Self {
86 part1: grid.iter().max().copied().unwrap_or(0),
87 part2: grid.iter().filter(|&&d| d >= 1000).count() as u16,
88 })
89 }
90
91 #[must_use]
92 pub fn part1(&self) -> u16 {
93 self.part1
94 }
95
96 #[must_use]
97 pub fn part2(&self) -> u16 {
98 self.part2
99 }
100}
101
102examples!(Day20 -> (u16, u16) [
103 {input: "^WNE$", part1: 3},
104 {input: "^ENWWW(NEEE|SSE(EE|N))$", part1: 10},
105 {input: "^ENNWSWW(NEWS|)SSSEEN(WNSE|)EE(SWEN|)NNN$", part1: 18},
106 {input: "^ESSWWN(E|NNENN(EESS(WNSE|)SSS|WWWSSSSE(SW|NNNE)))$", part1: 23},
107 {input: "^WSSEESWWWNW(S|NENNEEEENN(ESSSSW(NWSW|SSEN)|WSWWN(E|WWS(E|SS))))$", part1: 31},
108]);