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 match next {
49 10 => {
51 y += 1;
52 }
53 35 => {
55 scaffold.insert(Point::new(x, y));
56 }
57 60 => {
59 position = Point::new(x, y);
60 direction = LEFT;
61 }
62 62 => {
64 position = Point::new(x, y);
65 direction = RIGHT;
66 }
67 94 => {
69 position = Point::new(x, y);
70 direction = UP;
71 }
72 118 => {
74 position = Point::new(x, y);
75 direction = DOWN;
76 }
77 _ => (),
79 }
80 x = if next == 10 { 0 } else { x + 1 };
81 }
82
83 Input { code, scaffold, position, direction }
84}
85
86pub fn part1(input: &Input) -> i32 {
87 input
88 .scaffold
89 .iter()
90 .filter(|&point| ORTHOGONAL.iter().all(|&delta| input.scaffold.contains(&(*point + delta))))
91 .map(|point| point.x * point.y)
92 .sum()
93}
94
95pub fn part2(input: &Input) -> i64 {
96 let path = build_path(input);
97 let mut movement = Movement { routine: String::new(), functions: [None; 3] };
98
99 let _unused = compress(&path, &mut movement);
100
101 let mut rules = String::new();
103 let mut newline_ending = |s: &str| {
104 rules.push_str(s);
105 rules.pop();
106 rules.push('\n');
107 };
108
109 newline_ending(&movement.routine);
110 movement.functions.into_iter().flatten().for_each(newline_ending);
111
112 let mut modified = input.code.clone();
113 modified[0] = 2;
114
115 let mut computer = Computer::new(&modified);
116 computer.input_ascii(&rules);
117
118 visit(computer)
119}
120
121fn build_path(input: &Input) -> String {
125 let scaffold = &input.scaffold;
126 let mut position = input.position;
127 let mut direction = input.direction;
128 let mut path = String::new();
129
130 loop {
131 let left = direction.counter_clockwise();
132 let right = direction.clockwise();
133
134 if scaffold.contains(&(position + left)) {
135 direction = left;
136 } else if scaffold.contains(&(position + right)) {
137 direction = right;
138 } else {
139 break path;
140 }
141
142 let mut next = position + direction;
143 let mut magnitude = 0;
144
145 while scaffold.contains(&next) {
146 position = next;
147 next += direction;
148 magnitude += 1;
149 }
150
151 let direction = if direction == left { 'L' } else { 'R' };
152 let _ = write!(path, "{direction},{magnitude},");
153 }
154}
155
156fn compress<'a>(path: &'a str, movement: &mut Movement<'a>) -> ControlFlow<()> {
162 if path.is_empty() {
164 return ControlFlow::Break(());
165 }
166 if movement.routine.len() > 21 {
168 return ControlFlow::Continue(());
169 }
170
171 for (i, &name) in ['A', 'B', 'C'].iter().enumerate() {
172 movement.routine.push(name);
173 movement.routine.push(',');
174
175 if let Some(needle) = movement.functions[i] {
176 if let Some(remaining) = path.strip_prefix(needle) {
178 compress(remaining, movement)?;
179 }
180 } else {
181 for (needle, remaining) in segments(path) {
183 movement.functions[i] = Some(needle);
184 compress(remaining, movement)?;
185 movement.functions[i] = None;
186 }
187 }
188
189 movement.routine.pop();
190 movement.routine.pop();
191 }
192
193 ControlFlow::Continue(())
194}
195
196fn segments(path: &str) -> impl Iterator<Item = (&str, &str)> {
198 path.bytes()
199 .enumerate()
200 .filter_map(|(i, b)| (b == b',').then_some(i))
202 .take_while(|&i| i < 21)
204 .map(|i| path.split_at(i + 1))
206 .skip(1)
208 .step_by(2)
209}
210
211#[cfg(not(feature = "frivolity"))]
212fn visit(mut computer: Computer) -> i64 {
213 computer.input_ascii("n\n");
215
216 let mut result = 0;
217 while let State::Output(next) = computer.run() {
218 result = next;
219 }
220 result
221}
222
223#[cfg(feature = "frivolity")]
225fn visit(mut computer: Computer) -> i64 {
226 use crate::util::ansi::*;
227 use std::thread::sleep;
228 use std::time::Duration;
229
230 let mut result = 0;
231 let mut previous = ' ';
232 let mut buffer = String::new();
233
234 computer.input_ascii("y\n");
236
237 while let State::Output(next) = computer.run() {
238 result = next;
239 let ascii = (next as u8) as char;
240
241 match ascii {
243 '^' | 'v' | '<' | '>' => {
244 let _ = write!(&mut buffer, "{BOLD}{YELLOW}{ascii}{RESET}");
245 }
246 _ => buffer.push(ascii),
247 }
248
249 if ascii == '\n' && previous == '\n' {
251 print!("{HOME}{CLEAR}{buffer}");
252 sleep(Duration::from_millis(25));
253 buffer.clear();
254 }
255
256 previous = ascii;
257 }
258
259 result
260}