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 let Input { scaffold, .. } = input;
88 let mut result = 0;
89
90 for &point in scaffold {
91 if ORTHOGONAL.iter().all(|&delta| scaffold.contains(&(point + delta))) {
92 result += point.x * point.y;
93 }
94 }
95
96 result
97}
98
99pub fn part2(input: &Input) -> i64 {
100 let path = build_path(input);
101 let mut movement = Movement { routine: String::new(), functions: [None; 3] };
102
103 let _unused = compress(&path, &mut movement);
104
105 let mut rules = String::new();
107 let mut newline_ending = |s: &str| {
108 rules.push_str(s);
109 rules.pop();
110 rules.push('\n');
111 };
112
113 newline_ending(&movement.routine);
114 movement.functions.into_iter().flatten().for_each(newline_ending);
115
116 let mut modified = input.code.clone();
117 modified[0] = 2;
118
119 let mut computer = Computer::new(&modified);
120 computer.input_ascii(&rules);
121
122 visit(computer)
123}
124
125fn build_path(input: &Input) -> String {
129 let scaffold = &input.scaffold;
130 let mut position = input.position;
131 let mut direction = input.direction;
132 let mut path = String::new();
133
134 loop {
135 let left = direction.counter_clockwise();
136 let right = direction.clockwise();
137
138 if scaffold.contains(&(position + left)) {
139 direction = left;
140 } else if scaffold.contains(&(position + right)) {
141 direction = right;
142 } else {
143 break path;
144 }
145
146 let mut next = position + direction;
147 let mut magnitude = 0;
148
149 while scaffold.contains(&next) {
150 position = next;
151 next += direction;
152 magnitude += 1;
153 }
154
155 let direction = if direction == left { 'L' } else { 'R' };
156 let _ = write!(path, "{direction},{magnitude},");
157 }
158}
159
160fn compress<'a>(path: &'a str, movement: &mut Movement<'a>) -> ControlFlow<()> {
166 if path.is_empty() {
168 return ControlFlow::Break(());
169 }
170 if movement.routine.len() > 21 {
172 return ControlFlow::Continue(());
173 }
174
175 for (i, &name) in ['A', 'B', 'C'].iter().enumerate() {
176 movement.routine.push(name);
177 movement.routine.push(',');
178
179 if let Some(needle) = movement.functions[i] {
180 if let Some(remaining) = path.strip_prefix(needle) {
182 compress(remaining, movement)?;
183 }
184 } else {
185 for (needle, remaining) in segments(path) {
187 movement.functions[i] = Some(needle);
188 compress(remaining, movement)?;
189 movement.functions[i] = None;
190 }
191 }
192
193 movement.routine.pop();
194 movement.routine.pop();
195 }
196
197 ControlFlow::Continue(())
198}
199
200fn segments(path: &str) -> impl Iterator<Item = (&str, &str)> {
202 path.bytes()
203 .enumerate()
204 .filter_map(|(i, b)| (b == b',').then_some(i))
206 .take_while(|&i| i < 21)
208 .map(|i| path.split_at(i + 1))
210 .skip(1)
212 .step_by(2)
213}
214
215#[cfg(not(feature = "frivolity"))]
216fn visit(mut computer: Computer) -> i64 {
217 computer.input_ascii("n\n");
219
220 let mut result = 0;
221 while let State::Output(next) = computer.run() {
222 result = next;
223 }
224 result
225}
226
227#[cfg(feature = "frivolity")]
229fn visit(mut computer: Computer) -> i64 {
230 use crate::util::ansi::*;
231 use std::thread::sleep;
232 use std::time::Duration;
233
234 let mut result = 0;
235 let mut previous = ' ';
236 let mut buffer = String::new();
237
238 computer.input_ascii("y\n");
240
241 while let State::Output(next) = computer.run() {
242 result = next;
243 let ascii = (next as u8) as char;
244
245 match ascii {
247 '^' | 'v' | '<' | '>' => {
248 let _ = write!(&mut buffer, "{BOLD}{YELLOW}{ascii}{RESET}");
249 }
250 _ => buffer.push(ascii),
251 }
252
253 if ascii == '\n' && previous == '\n' {
255 print!("{HOME}{CLEAR}{buffer}");
256 sleep(Duration::from_millis(25));
257 buffer.clear();
258 }
259
260 previous = ascii;
261 }
262
263 result
264}