1use utils::prelude::*;
2
3#[derive(Clone, Debug)]
5pub struct Day04<'a> {
6 input: Vec<(&'a [u8], u32, &'a [u8])>,
7}
8
9impl<'a> Day04<'a> {
10 pub fn new(input: &'a str, _: InputType) -> Result<Self, InputError> {
11 let mut rooms = parser::take_while1(|&x| matches!(x, b'a'..=b'z' | b'-'))
12 .then(parser::u32())
13 .then(
14 parser::take_while1(u8::is_ascii_lowercase)
15 .with_prefix(b'[')
16 .with_suffix(b']'),
17 )
18 .parse_lines(input)?;
19
20 rooms.retain(|&(name, _, checksum)| {
21 let mut counts = [0; 26];
22 for &c in name {
23 if c.is_ascii_lowercase() {
24 counts[(c - b'a') as usize] += 1;
25 }
26 }
27
28 for &c in checksum {
29 let (letter, _) = counts
32 .iter()
33 .enumerate()
34 .rev()
35 .max_by_key(|&(_, &c)| c)
36 .unwrap();
37
38 if c != b'a' + letter as u8 {
39 return false;
40 }
41 counts[letter] = 0;
42 }
43
44 true
45 });
46
47 Ok(Self { input: rooms })
48 }
49
50 #[must_use]
51 pub fn part1(&self) -> u32 {
52 self.input.iter().map(|&r| r.1).sum()
53 }
54
55 #[must_use]
56 pub fn part2(&self) -> u32 {
57 const NAME: [u8; 25] = *b"northpole-object-storage-";
58
59 self.input
60 .iter()
61 .find(|&&(name, sector_id, _)| {
62 name.len() == NAME.len()
63 && name.iter().enumerate().all(|(i, &c)| {
64 if c == b'-' {
65 NAME[i] == b'-'
66 } else {
67 ((c - b'a' + (sector_id % 26) as u8) % 26 + b'a') == NAME[i]
68 }
69 })
70 })
71 .unwrap()
72 .1
73 }
74}
75
76examples!(Day04<'_> -> (u32, u32) [
77 {
78 input: "aaaaa-bbb-z-y-x-123[abxyz]\n\
79 a-b-c-d-e-f-g-h-987[abcde]\n\
80 not-a-real-room-404[oarel]\n\
81 totally-real-room-200[decoy]",
82 part1: 1514,
83 },
84]);