1use super::intcode::*;
22use crate::util::hash::*;
23use crate::util::parse::*;
24use crate::util::point::*;
25use std::fmt::Write as _;
26use std::iter::once;
27use std::ops::ControlFlow;
28
29type Input = (FastSet<Point>, i64);
30
31struct Movement<'a> {
32 routine: String,
33 functions: [Option<&'a str>; 3],
34}
35
36pub fn parse(input: &str) -> Input {
38 let code: Vec<_> = once(2).chain(input.iter_signed().skip(1)).collect();
41 let mut computer = Computer::new(&code);
42
43 let mut x = 0;
44 let mut y = 0;
45 let mut scaffold = FastSet::new();
46 let mut position = ORIGIN;
47 let mut direction = ORIGIN;
48
49 while let State::Output(next) = computer.run() {
50 let next = next as u8;
51 let point = Point::new(x, y);
52
53 match next {
54 b'\n' => {
55 y += 1;
56 x = 0;
57 continue;
58 }
59 b'#' => {
60 scaffold.insert(point);
61 }
62 b'<' | b'>' | b'^' | b'v' => {
63 scaffold.insert(point);
64 position = point;
65 direction = Point::from(next);
66 }
67 _ => (),
68 }
69
70 x += 1;
71 }
72
73 let path = build_path(&scaffold, position, direction);
75 let mut movement = Movement { routine: String::new(), functions: [None; 3] };
76
77 let _unused = compress(&path, &mut movement);
78
79 let mut rules = String::new();
81 let parts = once(movement.routine.as_str()).chain(movement.functions.into_iter().flatten());
82 for s in parts {
83 rules.push_str(s);
84 rules.pop();
85 rules.push('\n');
86 }
87
88 computer.input_ascii(&rules);
89 let score = visit(computer);
90
91 (scaffold, score)
92}
93
94pub fn part1(input: &Input) -> i32 {
95 let (scaffold, _) = input;
96 scaffold
97 .iter()
98 .filter(|&point| ORTHOGONAL.iter().all(|&delta| scaffold.contains(&(*point + delta))))
99 .map(|point| point.x * point.y)
100 .sum()
101}
102
103pub fn part2(input: &Input) -> i64 {
104 input.1
105}
106
107fn build_path(scaffold: &FastSet<Point>, mut position: Point, mut direction: Point) -> String {
111 let mut path = String::new();
112
113 loop {
114 let left = direction.counter_clockwise();
115 let right = direction.clockwise();
116
117 if scaffold.contains(&(position + left)) {
118 direction = left;
119 } else if scaffold.contains(&(position + right)) {
120 direction = right;
121 } else {
122 break path;
123 }
124
125 let mut next = position + direction;
126 let mut magnitude = 0;
127
128 while scaffold.contains(&next) {
129 position = next;
130 next += direction;
131 magnitude += 1;
132 }
133
134 let direction = if direction == left { 'L' } else { 'R' };
135 let _ = write!(path, "{direction},{magnitude},");
136 }
137}
138
139fn compress<'a>(path: &'a str, movement: &mut Movement<'a>) -> ControlFlow<()> {
145 if path.is_empty() {
147 return ControlFlow::Break(());
148 }
149 if movement.routine.len() > 21 {
151 return ControlFlow::Continue(());
152 }
153
154 for (i, &name) in ['A', 'B', 'C'].iter().enumerate() {
155 movement.routine.push(name);
156 movement.routine.push(',');
157
158 if let Some(needle) = movement.functions[i] {
159 if let Some(remaining) = path.strip_prefix(needle) {
161 compress(remaining, movement)?;
162 }
163 } else {
164 for (needle, remaining) in segments(path) {
166 movement.functions[i] = Some(needle);
167 compress(remaining, movement)?;
168 movement.functions[i] = None;
169 }
170 }
171
172 movement.routine.pop();
173 movement.routine.pop();
174 }
175
176 ControlFlow::Continue(())
177}
178
179fn segments(path: &str) -> impl Iterator<Item = (&str, &str)> {
181 path.bytes()
182 .enumerate()
183 .filter_map(|(i, b)| (b == b',').then_some(i))
185 .take_while(|&i| i < 21)
187 .map(|i| path.split_at(i + 1))
189 .skip(1)
191 .step_by(2)
192}
193
194#[cfg(not(feature = "frivolity"))]
195fn visit(mut computer: Computer) -> i64 {
196 computer.input_ascii("n\n");
198
199 let mut result = 0;
200 while let State::Output(next) = computer.run() {
201 result = next;
202 }
203 result
204}
205
206#[cfg(feature = "frivolity")]
208fn visit(mut computer: Computer) -> i64 {
209 use crate::util::ansi::*;
210 use std::thread::sleep;
211 use std::time::Duration;
212
213 let mut result = 0;
214 let mut previous = ' ';
215 let mut buffer = String::new();
216
217 computer.input_ascii("y\n");
219
220 while let State::Output(next) = computer.run() {
221 result = next;
222 let ascii = (next as u8) as char;
223
224 match ascii {
226 '^' | 'v' | '<' | '>' => {
227 let _ = write!(&mut buffer, "{BOLD}{YELLOW}{ascii}{RESET}");
228 }
229 _ => buffer.push(ascii),
230 }
231
232 if ascii == '\n' && previous == '\n' {
234 print!("{HOME}{CLEAR}{buffer}");
235 sleep(Duration::from_millis(25));
236 buffer.clear();
237 }
238
239 previous = ascii;
240 }
241
242 result
243}