year2018/
day04.rs

1use std::collections::HashMap;
2use utils::prelude::*;
3
4/// Analysing guard sleep schedules.
5#[derive(Clone, Debug)]
6pub struct Day04 {
7    guards: Vec<Guard>,
8}
9
10#[derive(Clone, Debug)]
11struct Guard {
12    id: u32,
13    nights: Vec<u64>,
14    most_frequent_minute: u32,
15    most_frequent_count: u32,
16}
17
18#[derive(Debug)]
19enum Event {
20    BeginsShift(u32),
21    FallsAsleep,
22    WakesUp,
23}
24
25impl Day04 {
26    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
27        let mut lines: Vec<&str> = input.lines().collect();
28        lines.sort_unstable();
29
30        let mut current_guard_idx = None;
31        let mut guard_indexes = HashMap::new();
32        let mut guards = Vec::new();
33        for line in lines {
34            let (_, _, _, min, event) = parser::number_range(1u32..=12)
35                .with_prefix("[1518-")
36                .with_suffix(b'-')
37                .then(parser::number_range(1u32..=31).with_suffix(b' '))
38                .then(parser::number_range(0u32..=23).with_suffix(b':'))
39                .then(parser::number_range(0u32..=59).with_suffix("] "))
40                .then(parser::one_of((
41                    parser::u32()
42                        .with_prefix("Guard #")
43                        .with_suffix(" begins shift")
44                        .map(Event::BeginsShift),
45                    "falls asleep".map(|_| Event::FallsAsleep),
46                    "wakes up".map(|_| Event::WakesUp),
47                )))
48                .parse_complete(line)
49                .map_err(|e| InputError::new(input, line, e.into_source()))?;
50
51            if let Event::BeginsShift(id) = event {
52                let guard_idx = *guard_indexes.entry(id).or_insert_with(|| {
53                    let idx = guards.len();
54                    guards.push(Guard {
55                        id,
56                        nights: Vec::with_capacity(16),
57                        most_frequent_count: 0,
58                        most_frequent_minute: 0,
59                    });
60                    idx
61                });
62                guards[guard_idx].nights.push(0);
63                current_guard_idx = Some(guard_idx);
64                continue;
65            }
66
67            let Some(idx) = current_guard_idx else {
68                return Err(InputError::new(input, line, "invalid first event"));
69            };
70
71            let night = guards[idx].nights.last_mut().unwrap();
72            let mask = (1u64 << (60 - min)) - 1;
73            if let Event::FallsAsleep = event {
74                *night |= mask;
75            } else {
76                *night &= !mask;
77            }
78        }
79
80        if guards.is_empty() {
81            return Err(InputError::new(input, 0, "expected at least one guard"));
82        }
83
84        for guard in &mut guards {
85            (guard.most_frequent_minute, guard.most_frequent_count) = (0..60)
86                .map(|m| {
87                    (
88                        m,
89                        guard
90                            .nights
91                            .iter()
92                            .filter(|&&b| b & (1u64 << (59 - m)) != 0)
93                            .count() as u32,
94                    )
95                })
96                .max_by_key(|&(_, c)| c)
97                .unwrap();
98        }
99
100        Ok(Self { guards })
101    }
102
103    #[must_use]
104    pub fn part1(&self) -> u32 {
105        self.select_guard(|g| g.nights.iter().map(|b| b.count_ones()).sum())
106    }
107
108    #[must_use]
109    pub fn part2(&self) -> u32 {
110        self.select_guard(|g| g.most_frequent_count)
111    }
112
113    fn select_guard(&self, key_fn: impl Fn(&&Guard) -> u32) -> u32 {
114        let guard = self.guards.iter().max_by_key(key_fn).unwrap();
115        guard.id * guard.most_frequent_minute
116    }
117}
118
119examples!(Day04 -> (u32, u32) [
120    {file: "day04_example0.txt", part1: 240, part2: 4455},
121]);