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