advent_of_code/year2016/
day04.rs

1use crate::input::Input;
2
3pub fn solve(input: &Input) -> Result<u32, String> {
4    const NUM_ASCII_LOWERCASE: usize = 26;
5
6    let mut sector_ids_sum = 0;
7
8    for (line_idx, line) in input.text.lines().enumerate() {
9        let on_error = || format!("Line {}: Invalid input", line_idx + 1);
10
11        let (room_name, sector_id_and_checksum) = line.rsplit_once('-').ok_or_else(on_error)?;
12
13        let (sector_id, stated_checksum) = sector_id_and_checksum
14            .split_once('[')
15            .and_then(|(id, checksum_str)| {
16                let checksum: usize =
17                    checksum_str
18                        .bytes()
19                        .take(5)
20                        .enumerate()
21                        .try_fold(0, |acc, (idx, b)| {
22                            b.is_ascii_lowercase()
23                                .then_some(acc * idx * NUM_ASCII_LOWERCASE + (b - b'a') as usize)
24                        })?;
25
26                Some((id.parse::<u32>().ok()?, checksum))
27            })
28            .ok_or_else(on_error)?;
29
30        if input.is_part_one() {
31            let mut char_frequency: [(u8, u32); NUM_ASCII_LOWERCASE] =
32                std::array::from_fn(|i| (i as u8, 0));
33            for c in room_name.bytes().filter(u8::is_ascii_lowercase) {
34                char_frequency[(c - b'a') as usize].1 += 1;
35            }
36
37            char_frequency.sort_unstable_by(|(c1, f1), (c2, f2)| f2.cmp(f1).then(c1.cmp(c2)));
38
39            let computed_checksum = char_frequency
40                .iter()
41                .take(5)
42                .enumerate()
43                .fold(0, |acc, (idx, (c, _f))| {
44                    acc * idx * NUM_ASCII_LOWERCASE + *c as usize
45                });
46
47            if computed_checksum == stated_checksum {
48                sector_ids_sum += sector_id;
49            }
50        } else {
51            let desired_name = b"northpole object storage".iter();
52            if room_name
53                .bytes()
54                .map(|a| match a {
55                    b'-' => b' ',
56                    _ if a.is_ascii_lowercase() => {
57                        ((u32::from(a) - 'a' as u32 + sector_id) % (NUM_ASCII_LOWERCASE as u32))
58                            as u8
59                            + b'a'
60                    }
61                    _ => u8::MAX,
62                })
63                .zip(desired_name)
64                .all(|(a, &b)| a == b)
65            {
66                return Ok(sector_id);
67            }
68        }
69    }
70
71    Ok(sector_ids_sum)
72}
73
74#[test]
75pub fn tests() {
76    use crate::input::{test_part_one_no_allocations, test_part_two_no_allocations};
77
78    let real_input = include_str!("day04_input.txt");
79
80    test_part_one_no_allocations!(real_input => 245_102);
81    test_part_two_no_allocations!(real_input => 324);
82}