year2016/
day06.rs

1use utils::prelude::*;
2
3/// Decoding repetition code.
4#[derive(Clone, Debug)]
5pub struct Day06 {
6    frequencies: Vec<[u32; 26]>,
7}
8
9impl Day06 {
10    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
11        if let Some(b) = input.bytes().find(|b| !matches!(b, b'a'..=b'z' | b'\n')) {
12            return Err(InputError::new(
13                input,
14                b as char,
15                "expected lowercase letter or newline",
16            ));
17        }
18
19        let Some(length) = input.find('\n') else {
20            return Err(InputError::new(input, 0, "expected at least one newline"));
21        };
22
23        let mut frequencies = vec![[0; 26]; length];
24        for l in input.lines() {
25            if l.len() != length {
26                return Err(InputError::new(input, 0, "expected line length to match"));
27            }
28
29            for (i, c) in l.bytes().enumerate() {
30                frequencies[i][(c - b'a') as usize] += 1;
31            }
32        }
33
34        Ok(Self { frequencies })
35    }
36
37    #[must_use]
38    pub fn part1(&self) -> String {
39        // Find the most frequent letter in each column
40        self.decode_message(|c| c)
41    }
42
43    #[must_use]
44    pub fn part2(&self) -> String {
45        // Find the least frequent letter in each column, correctly handling letters that don't
46        // appear in the column (which happens in the example)
47        self.decode_message(|c| if c == 0 { 0 } else { u32::MAX - c })
48    }
49
50    fn decode_message(&self, count_map_fn: impl Fn(u32) -> u32) -> String {
51        self.frequencies
52            .iter()
53            .map(|counts| {
54                (counts
55                    .iter()
56                    .enumerate()
57                    .rev()
58                    .max_by_key(|&(_, &c)| count_map_fn(c))
59                    .unwrap()
60                    .0 as u8
61                    + b'a') as char
62            })
63            .collect()
64    }
65}
66
67examples!(Day06 -> (&'static str, &'static str) [
68    {file: "day06_example0.txt", part1: "easter", part2: "advent"},
69]);