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