Skip to main content

year2019/
day11.rs

1use crate::intcode::Interpreter;
2use crate::intcode::features::Day09Features;
3use utils::geometry::{Direction, Vec2};
4use utils::prelude::*;
5
6/// Recognizing text painted by interpreting machine code.
7#[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) []);