aoc/year2018/
day04.rs

1//! # Repose Record
2use crate::util::hash::*;
3use crate::util::parse::*;
4
5type Input = FastMap<usize, [u32; 60]>;
6
7pub fn parse(input: &str) -> Input {
8    // Records need to be in chronological order.
9    let mut records: Vec<_> = input.lines().map(str::as_bytes).collect();
10    records.sort_unstable();
11
12    // Build each sleep schedule
13    let mut id = 0;
14    let mut start = 0;
15    let mut guards = FastMap::new();
16
17    for record in records {
18        match record.len() {
19            31 => start = to_index(&record[15..17]),
20            27 => {
21                let end = to_index(&record[15..17]);
22                let minutes = guards.entry(id).or_insert_with(|| [0; 60]);
23                (start..end).for_each(|i| minutes[i] += 1);
24            }
25            _ => id = to_index(&record[26..record.len() - 13]),
26        }
27    }
28
29    guards
30}
31
32/// Find the guard with the greatest total minutes asleep.
33pub fn part1(input: &Input) -> usize {
34    choose(input, |(_, m)| m.iter().sum())
35}
36
37/// Find the guard with the highest single minute asleep.
38pub fn part2(input: &Input) -> usize {
39    choose(input, |(_, m)| *m.iter().max().unwrap())
40}
41
42fn choose(input: &Input, strategy: impl Fn(&(&usize, &[u32; 60])) -> u32) -> usize {
43    // Find the guard using a specific strategy.
44    let (id, minutes) = input.iter().max_by_key(strategy).unwrap();
45    // Find the minute spent asleep the most
46    let (minute, _) = minutes.iter().enumerate().max_by_key(|(_, m)| **m).unwrap();
47    // Return result
48    id * minute
49}
50
51fn to_index(slice: &[u8]) -> usize {
52    slice.iter().fold(0, |acc, n| 10 * acc + n.to_decimal() as usize)
53}