summaryrefslogtreecommitdiff
path: root/2018/src/day4.rs
blob: f5e219f7f7041476f697af6ac4cdda8e1073c98a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::io;
use std::io::BufRead;
use std::collections::HashMap;
use std::cmp::max;
use regex::Regex;

#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum Action {
    Begin(i32),
    Sleep,
    Wake
}

#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Record {
    year: i32,
    month: i32,
    day: i32,
    hour: i32,
    minute: i32,
    action: Action
}

struct Guard {
    id: i32,
    sleep_times: Vec<(i32, i32)>,  // inclusive-exclusive
    asleep: [i32; 60]
}

fn parse_action(line: &str) -> Action {
    if line.chars().next() == Some('G') {
        let idx = 7 + line[7..].find(' ').unwrap();
        Action::Begin(line[7 .. idx].parse().unwrap())
    } else if line.chars().next() == Some('f') {
        Action::Sleep
    } else {
        Action::Wake
    }
}

fn parse_line(line: &str) -> Record {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"^\[(\d+)-(\d+)-(\d+) (\d+):(\d+)\] (.*)$").unwrap();
    }

    let caps = RE.captures(line).unwrap();
    Record {
        year: caps.get(1).unwrap().as_str().parse().unwrap(),
        month: caps.get(2).unwrap().as_str().parse().unwrap(),
        day: caps.get(3).unwrap().as_str().parse().unwrap(),
        hour: caps.get(4).unwrap().as_str().parse().unwrap(),
        minute: caps.get(5).unwrap().as_str().parse().unwrap(),
        action: parse_action(caps.get(6).unwrap().as_str())
    }
}

fn argmax<T: Ord>(v: &[T]) -> Option<usize> {
    if v.len() == 0 {
        None
    } else {
        let mut maxat = 0;
        for i in 1..v.len() {
            if v[i] > v[maxat] {
                maxat = i;
            }
        }
        Some(maxat)
    }
}

pub fn main<T: BufRead>(reader: T) -> io::Result<(String, String)> {
    let mut records: Vec<Record> = reader.lines().map(|l| parse_line(&l.unwrap())).collect();

    records.sort_unstable();
    let mut guards = HashMap::new();

    {
        let mut current_id = 0;
        let mut sleep_start = 0;

        for record in records.iter() {
            match record.action {
                Action::Begin(id) => {
                    if let None = guards.get(&id) {
                        guards.insert(id, Guard {id: id, sleep_times: Vec::new(), asleep: [0; 60]});
                    }
                    current_id = id;
                },
                Action::Sleep => sleep_start = record.minute,
                Action::Wake =>
                    guards.get_mut(&current_id).unwrap()
                        .sleep_times.push((sleep_start, record.minute))
            }
        }
    }

    let mut max_sleep = -1;
    let mut max_sleep_at = -1;
    let mut max_times = -1;
    let mut max_times_at = -1;

    for mut guard in guards.values_mut() {
        let mut sleep = 0;
        let mut times = 0;

        for (from, to) in guard.sleep_times.iter() {
            for i in *from..*to {
                guard.asleep[i as usize] += 1;
                times = max(times, guard.asleep[i as usize]);
            }
            sleep += to - from;
        }

        if sleep > max_sleep {
            max_sleep = sleep;
            max_sleep_at = guard.id;
        }

        if times > max_times {
            max_times = times;
            max_times_at = guard.id;
        }
    }

    let sleepy_guard = guards.get(&max_sleep_at).unwrap();
    let max_minute = argmax(&sleepy_guard.asleep).unwrap() as i32;
    let part1 = max_minute * sleepy_guard.id;

    let consistent_guard = guards.get(&max_times_at).unwrap();
    let max_minute = argmax(&consistent_guard.asleep).unwrap() as i32;
    let part2 = max_minute * consistent_guard.id;

    Ok((part1.to_string(), part2.to_string()))
}