summaryrefslogtreecommitdiff
path: root/2018/src/day4.rs
diff options
context:
space:
mode:
Diffstat (limited to '2018/src/day4.rs')
-rw-r--r--2018/src/day4.rs134
1 files changed, 134 insertions, 0 deletions
diff --git a/2018/src/day4.rs b/2018/src/day4.rs
new file mode 100644
index 0000000..90cab72
--- /dev/null
+++ b/2018/src/day4.rs
@@ -0,0 +1,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<()> {
+ 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;
+ println!("{}", max_minute * sleepy_guard.id);
+
+ let consistent_guard = guards.get(&max_times_at).unwrap();
+ let max_minute = argmax(&consistent_guard.asleep).unwrap() as i32;
+ println!("{}", max_minute * consistent_guard.id);
+
+ Ok(())
+}