1use crate::intcode::Interpreter;
2use crate::intcode::features::Day09Features;
3use utils::geometry::{Direction, Vec2};
4use utils::prelude::*;
5
6#[derive(Clone, Debug)]
8pub struct Day11 {
9 base: Interpreter,
10}
11
12#[derive(Copy, Clone, Debug, PartialEq)]
13enum Panel {
14 Unpainted,
15 White,
16 Black,
17}
18
19const WIDTH: usize = 200;
20const SIZE: usize = WIDTH * WIDTH;
21
22impl Day11 {
23 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
24 Ok(Self {
25 base: Interpreter::parse(input, 1)?,
26 })
27 }
28
29 #[must_use]
30 pub fn part1(&self) -> usize {
31 let mut grid = [Panel::Unpainted; SIZE];
32 self.paint(&mut grid)
33 }
34
35 #[must_use]
36 pub fn part2(&self) -> String {
37 let mut grid = [Panel::Unpainted; SIZE];
38 grid[(WIDTH / 2) * WIDTH + (WIDTH / 2)] = Panel::White;
39 self.paint(&mut grid);
40
41 let (mut min_x, mut min_y) = (WIDTH, WIDTH);
42 for (y, row) in grid.chunks_exact(WIDTH).enumerate() {
43 for (x, &panel) in row.iter().enumerate() {
44 if panel == Panel::White {
45 min_x = min_x.min(x);
46 min_y = min_y.min(y);
47 }
48 }
49 }
50
51 let mut output = String::with_capacity(8);
52 for x in (min_x..min_x + 40).step_by(5) {
53 let mut letter = 0;
54 for y in (min_y..min_y + 6).rev() {
55 for dx in 0..5 {
56 letter = (letter << 1) | u32::from(grid[y * WIDTH + x + dx] == Panel::White);
57 }
58 }
59 output.push(crate::Day08::ocr(letter));
60 }
61 output
62 }
63
64 fn paint(&self, grid: &mut [Panel; SIZE]) -> usize {
65 let mut interpreter = self.base.clone();
66 let mut pos = Vec2::new(WIDTH as i32 / 2, WIDTH as i32 / 2);
67 let mut dir = Direction::Up;
68 let mut painted = 0;
69
70 loop {
71 let index = pos.y as usize * WIDTH + pos.x as usize;
72 interpreter.push_input(i64::from(grid[index] == Panel::White));
73
74 let mut next_output = || match interpreter.next_output::<Day09Features>() {
75 Some(x @ 0..=1) => Some(x as u8),
76 Some(_) => panic!("no solution found: program returned invalid output"),
77 None => None,
78 };
79 let (Some(color), Some(turn)) = (next_output(), next_output()) else {
80 return painted;
81 };
82
83 if grid[index] == Panel::Unpainted {
84 painted += 1;
85 }
86 grid[index] = if color == 0 {
87 Panel::Black
88 } else {
89 Panel::White
90 };
91
92 if turn == 0 {
93 dir = dir.turn_left();
94 } else {
95 dir = dir.turn_right();
96 }
97 pos += Vec2::from(dir);
98 if pos.x < 0 || pos.x >= WIDTH as i32 || pos.y < 0 || pos.y >= WIDTH as i32 {
99 panic!("robot left grid bounds");
100 }
101 }
102 }
103}
104
105examples!(Day11 -> (usize, &'static str) []);