1use std::collections::HashMap;
2use utils::prelude::*;
3
4#[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]);