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 let (rows, []) = grid.as_chunks::<WIDTH>() else {
43 unreachable!("SIZE is a multiple of WIDTH");
44 };
45 for (y, row) in rows.iter().enumerate() {
46 for (x, &panel) in row.iter().enumerate() {
47 if panel == Panel::White {
48 min_x = min_x.min(x);
49 min_y = min_y.min(y);
50 }
51 }
52 }
53
54 let mut output = String::with_capacity(8);
55 for x in (min_x..min_x + 40).step_by(5) {
56 let mut letter = 0;
57 for y in (min_y..min_y + 6).rev() {
58 for dx in 0..5 {
59 letter = (letter << 1) | u32::from(grid[y * WIDTH + x + dx] == Panel::White);
60 }
61 }
62 output.push(crate::Day08::ocr(letter));
63 }
64 output
65 }
66
67 fn paint(&self, grid: &mut [Panel; SIZE]) -> usize {
68 let mut interpreter = self.base.clone();
69 let mut pos = Vec2::new(WIDTH as i32 / 2, WIDTH as i32 / 2);
70 let mut dir = Direction::Up;
71 let mut painted = 0;
72
73 loop {
74 let index = pos.y as usize * WIDTH + pos.x as usize;
75 interpreter.push_input(i64::from(grid[index] == Panel::White));
76
77 let mut next_output = || match interpreter.next_output::<Day09Features>() {
78 Some(x @ 0..=1) => Some(x as u8),
79 Some(_) => panic!("no solution found: program returned invalid output"),
80 None => None,
81 };
82 let (Some(color), Some(turn)) = (next_output(), next_output()) else {
83 return painted;
84 };
85
86 if grid[index] == Panel::Unpainted {
87 painted += 1;
88 }
89 grid[index] = if color == 0 {
90 Panel::Black
91 } else {
92 Panel::White
93 };
94
95 if turn == 0 {
96 dir = dir.turn_left();
97 } else {
98 dir = dir.turn_right();
99 }
100 pos += Vec2::from(dir);
101 if pos.x < 0 || pos.x >= WIDTH as i32 || pos.y < 0 || pos.y >= WIDTH as i32 {
102 panic!("robot left grid bounds");
103 }
104 }
105 }
106}
107
108examples!(Day11 -> (usize, &'static str) []);