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        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) []);