Skip to main content

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        let (rows, []) = self.image.as_chunks::<WIDTH>() else {
67            unreachable!("LAYER_LEN is a multiple of WIDTH");
68        };
69
70        for x in (0..WIDTH).step_by(5) {
71            let mut letter = 0;
72            for row in rows {
73                for &pixel in &row[x..x + 5] {
74                    letter = (letter << 1) | u32::from(pixel == b'1');
75                }
76            }
77
78            output.push(Self::ocr(letter));
79        }
80
81        output
82    }
83
84    #[inline]
85    pub(crate) fn ocr(letter: u32) -> char {
86        //  ##  ###   ##  #### ####  ##  #  #   ## #  # #    ###  ###  #  # #   # ####
87        // #  # #  # #  # #    #    #  # #  #    # # #  #    #  # #  # #  # #   #    #
88        // #  # ###  #    ###  ###  #    ####    # ##   #    #  # #  # #  #  # #    #
89        // #### #  # #    #    #    # ## #  #    # # #  #    ###  ###  #  #   #    #
90        // #  # #  # #  # #    #    #  # #  # #  # # #  #    #    # #  #  #   #   #
91        // #  # ###   ##  #### #     ### #  #  ##  #  # #### #    #  #  ##    #   ####
92        match letter {
93            //111112222233333444445555566666
94            0b011001001010010111101001010010 => 'A',
95            0b111001001011100100101001011100 => 'B',
96            0b011001001010000100001001001100 => 'C',
97            0b111101000011100100001000011110 => 'E',
98            0b111101000011100100001000010000 => 'F',
99            0b011001001010000101101001001110 => 'G',
100            0b100101001011110100101001010010 => 'H',
101            0b001100001000010000101001001100 => 'J',
102            0b100101010011000101001010010010 => 'K',
103            0b100001000010000100001000011110 => 'L',
104            0b111001001010010111001000010000 => 'P',
105            0b111001001010010111001010010010 => 'R',
106            0b100101001010010100101001001100 => 'U',
107            0b100011000101010001000010000100 => 'Y',
108            0b111100001000100010001000011110 => 'Z',
109            _ => Self::unknown_letter(letter),
110        }
111    }
112
113    #[cold]
114    fn unknown_letter(letter: u32) -> char {
115        let mut display = String::new();
116        for b in (0..30).rev() {
117            display.push(if letter & (1 << b) == 0 { ' ' } else { '#' });
118            if b % 5 == 0 {
119                display.push('\n');
120            }
121        }
122        panic!("unknown letter {letter:#032b}:\n{display}");
123    }
124}
125
126examples!(Day08 -> (u32, &'static str) []);