year2019/
day08.rs

1use utils::prelude::*;
2
3/// Recognizing text formed by stacking layers.
4#[derive(Clone, Debug)]
5pub struct Day08 {
6    part1: u32,
7    image: [u8; LAYER_LEN],
8}
9
10const WIDTH: usize = 25;
11const HEIGHT: usize = 6;
12const LAYER_LEN: usize = WIDTH * HEIGHT;
13
14impl Day08 {
15    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
16        let (layers, remainder) = input.as_bytes().as_chunks::<LAYER_LEN>();
17        if layers.is_empty() || !remainder.is_empty() {
18            return Err(InputError::new(
19                input,
20                0,
21                "expected input length to be a multiple of the layer size",
22            ));
23        }
24
25        let mut image = [b'2'; LAYER_LEN];
26        let (mut min_zeroes, mut part1) = (u8::MAX, 0);
27
28        for layer in layers {
29            let (mut zeroes, mut ones, mut twos) = (0, 0, 0);
30            for (&b, o) in layer.iter().zip(image.iter_mut()) {
31                zeroes += u8::from(b == b'0');
32                ones += u8::from(b == b'1');
33                twos += u8::from(b == b'2');
34                *o = if *o == b'2' { b } else { *o };
35            }
36
37            if zeroes < min_zeroes {
38                min_zeroes = zeroes;
39                part1 = u32::from(ones) * u32::from(twos);
40            }
41
42            if zeroes + ones + twos != LAYER_LEN as u8 {
43                return Err(InputError::new(
44                    input,
45                    layer
46                        .iter()
47                        .copied()
48                        .find(|&b| b != b'0' && b != b'1' && b != b'2')
49                        .unwrap() as char,
50                    "expected '0', '1' or '2'",
51                ));
52            }
53        }
54
55        Ok(Self { part1, image })
56    }
57
58    #[must_use]
59    pub fn part1(&self) -> u32 {
60        self.part1
61    }
62
63    #[must_use]
64    pub fn part2(&self) -> String {
65        let mut output = String::with_capacity(WIDTH / 5);
66
67        for x in (0..WIDTH).step_by(5) {
68            let mut letter = 0;
69            for row in self.image.chunks_exact(WIDTH) {
70                for &pixel in &row[x..x + 5] {
71                    letter = (letter << 1) | u32::from(pixel == b'1');
72                }
73            }
74
75            output.push(Self::ocr(letter));
76        }
77
78        output
79    }
80
81    #[inline]
82    fn ocr(letter: u32) -> char {
83        //  ##  ###   ##  #### ####  ##  #  #   ## #  # #    ###  ###  #  # #   # ####
84        // #  # #  # #  # #    #    #  # #  #    # # #  #    #  # #  # #  # #   #    #
85        // #  # ###  #    ###  ###  #    ####    # ##   #    #  # #  # #  #  # #    #
86        // #### #  # #    #    #    # ## #  #    # # #  #    ###  ###  #  #   #    #
87        // #  # #  # #  # #    #    #  # #  # #  # # #  #    #    # #  #  #   #   #
88        // #  # ###   ##  #### #     ### #  #  ##  #  # #### #    #  #  ##    #   ####
89        match letter {
90            //111112222233333444445555566666
91            0b011001001010010111101001010010 => 'A',
92            0b111001001011100100101001011100 => 'B',
93            0b011001001010000100001001001100 => 'C',
94            0b111101000011100100001000011110 => 'E',
95            0b111101000011100100001000010000 => 'F',
96            0b011001001010000101101001001110 => 'G',
97            0b100101001011110100101001010010 => 'H',
98            0b001100001000010000101001001100 => 'J',
99            0b100101010011000101001010010010 => 'K',
100            0b100001000010000100001000011110 => 'L',
101            0b111001001010010111001000010000 => 'P',
102            0b111001001010010111001010010010 => 'R',
103            0b100101001010010100101001001100 => 'U',
104            0b100011000101010001000010000100 => 'Y',
105            0b111100001000100010001000011110 => 'Z',
106            _ => Self::unknown_letter(letter),
107        }
108    }
109
110    #[cold]
111    fn unknown_letter(letter: u32) -> char {
112        let mut display = String::new();
113        for b in (0..30).rev() {
114            display.push(if letter & (1 << b) == 0 { ' ' } else { '#' });
115            if b % 5 == 0 {
116                display.push('\n');
117            }
118        }
119        panic!("unknown letter {letter:#032b}:\n{display}");
120    }
121}
122
123examples!(Day08 -> (u32, &'static str) []);