advent_of_code/year2018/
day04.rs

1use crate::input::Input;
2use std::collections::HashMap;
3
4enum EntryType {
5    BeginShift { guard_id: u32 },
6    FallsAsleep,
7    WakesUp,
8}
9
10struct LogEntry {
11    minute: u32,
12    entry: EntryType,
13}
14
15fn parse_input(input_string: &str) -> Result<Vec<LogEntry>, String> {
16    let mut lines: Vec<&str> = input_string.lines().collect();
17    lines.sort_unstable();
18
19    lines
20        .iter()
21        .enumerate()
22        .map(|(line_index, line)| {
23            let error_message = || format!("Incorrect input at line {}", line_index + 1);
24            let parts: Vec<&str> = line.split_whitespace().collect();
25            if parts.len() < 4 || parts[1].len() != 6 {
26                return Err(error_message());
27            }
28
29            let minute = parts[1][3..5].parse().map_err(|_| error_message())?;
30
31            let entry = match *parts.last().ok_or("Internal error: No last value")? {
32                "shift" => EntryType::BeginShift {
33                    guard_id: parts[3][1..].parse().map_err(|_| error_message())?,
34                },
35                "asleep" => EntryType::FallsAsleep,
36                "up" => EntryType::WakesUp,
37                _ => {
38                    return Err(error_message());
39                }
40            };
41
42            Ok(LogEntry { minute, entry })
43        })
44        .collect()
45}
46
47pub fn solve(input: &Input) -> Result<u32, String> {
48    let entries = parse_input(input.text)?;
49
50    let mut sleepers = HashMap::new();
51    let mut current_guard = 0;
52    let mut start_minute = 0;
53
54    for log_entry in entries.iter() {
55        match log_entry.entry {
56            EntryType::BeginShift { guard_id } => current_guard = guard_id,
57            EntryType::FallsAsleep => start_minute = log_entry.minute,
58            EntryType::WakesUp => {
59                let sleep_record = sleepers.entry(current_guard).or_insert_with(|| vec![0; 61]);
60                for minute in start_minute..log_entry.minute {
61                    sleep_record[minute as usize] += 1;
62                }
63            }
64        }
65    }
66
67    if input.is_part_one() {
68        let (&most_sleepy_guard, sleep_record) = sleepers
69            .iter()
70            .max_by_key(|(_key, value)| value.iter().sum::<i32>())
71            .ok_or("Internal error: No most sleep guard")?;
72        let most_sleepy_minute = sleep_record
73            .iter()
74            .enumerate()
75            .max_by_key(|(_minute, count)| *count)
76            .ok_or("Internal error: No most sleepy minute")?
77            .0 as u32;
78
79        Ok(most_sleepy_guard * most_sleepy_minute)
80    } else {
81        let mut highest_sleep_count = -1;
82        let mut sleepiest_guard_id = 0;
83        let mut most_sleepy_minute = 0;
84        for (&guard_id, sleep_record) in sleepers.iter() {
85            let (sleepy_minute, &sleep_count) = sleep_record
86                .iter()
87                .enumerate()
88                .max_by_key(|(_minute, count)| *count)
89                .ok_or("No sleep record for guard")?;
90            if sleep_count > highest_sleep_count {
91                highest_sleep_count = sleep_count;
92                sleepiest_guard_id = guard_id;
93                most_sleepy_minute = sleepy_minute;
94            }
95        }
96        Ok(sleepiest_guard_id * most_sleepy_minute as u32)
97    }
98}
99
100#[test]
101fn tests() {
102    use crate::input::{test_part_one, test_part_two};
103
104    test_part_one!(
105            "[1518-11-01 00:00] Guard #10 begins shift
106[1518-11-01 00:05] falls asleep
107[1518-11-01 00:25] wakes up
108[1518-11-01 00:30] falls asleep
109[1518-11-01 00:55] wakes up
110[1518-11-01 23:58] Guard #99 begins shift
111[1518-11-02 00:40] falls asleep
112[1518-11-02 00:50] wakes up
113[1518-11-03 00:05] Guard #10 begins shift
114[1518-11-03 00:24] falls asleep
115[1518-11-03 00:29] wakes up
116[1518-11-04 00:02] Guard #99 begins shift
117[1518-11-04 00:36] falls asleep
118[1518-11-04 00:46] wakes up
119[1518-11-05 00:03] Guard #99 begins shift
120[1518-11-05 00:45] falls asleep
121[1518-11-05 00:55] wakes up"
122    => 240
123    );
124
125    let input = include_str!("day04_input.txt");
126    test_part_one!(input => 84834);
127
128    test_part_two!(
129            "[1518-11-01 00:00] Guard #10 begins shift
130[1518-11-01 00:05] falls asleep
131[1518-11-01 00:25] wakes up
132[1518-11-01 00:30] falls asleep
133[1518-11-01 00:55] wakes up
134[1518-11-01 23:58] Guard #99 begins shift
135[1518-11-02 00:40] falls asleep
136[1518-11-02 00:50] wakes up
137[1518-11-03 00:05] Guard #10 begins shift
138[1518-11-03 00:24] falls asleep
139[1518-11-03 00:29] wakes up
140[1518-11-04 00:02] Guard #99 begins shift
141[1518-11-04 00:36] falls asleep
142[1518-11-04 00:46] wakes up
143[1518-11-05 00:03] Guard #99 begins shift
144[1518-11-05 00:45] falls asleep
145[1518-11-05 00:55] wakes up"
146        =>4455
147    );
148
149    test_part_two!(input => 53427);
150}