From 1e51012d5e973420d26b5d28e6cdf726f88e4979 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Tue, 4 Dec 2018 13:41:45 +0100 Subject: [PATCH] Implement day 4. Nice code-reuse between 1 and 2. I'm not accustomed to this. --- 2018/Cargo.toml | 1 + 2018/src/day04.rs | 151 ++++++++++++++++++++++++++++++++++++++++++++++ 2018/src/main.rs | 5 +- 3 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 2018/src/day04.rs diff --git a/2018/Cargo.toml b/2018/Cargo.toml index 87e41a0..1a83267 100644 --- a/2018/Cargo.toml +++ b/2018/Cargo.toml @@ -6,3 +6,4 @@ authors = ["Bert Peters "] [dependencies] clap = "2.32" regex = "1.1.0" +chrono = "0.4.6" diff --git a/2018/src/day04.rs b/2018/src/day04.rs new file mode 100644 index 0000000..e05c509 --- /dev/null +++ b/2018/src/day04.rs @@ -0,0 +1,151 @@ +use std::io; +use std::io::BufRead; + +use chrono::DateTime; +use chrono::offset::TimeZone; +use chrono::offset::Utc; +use regex::Regex; + +use common; +use std::collections::HashMap; +use chrono::Timelike; + +#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Copy, Clone)] +enum EventType { + WAKE, + SLEEP, + SHIFT(usize), +} + +#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Copy, Clone)] +struct Event { + time: DateTime, + event: EventType, +} + +#[derive(Default)] +pub struct Day04 { + events: Vec, +} + +impl Day04 { + pub fn new() -> Day04 { + Default::default() + } + + fn read_events(&mut self, input: &mut io::Read) { + self.events.clear(); + let reader = io::BufReader::new(input); + + let scanner = Regex::new(r"^\[([^\]]+)\] (Guard #(\d+)|falls asleep|wakes up)").unwrap(); + + for line in reader.lines() { + let line = line.unwrap(); + let captures = scanner.captures(&line).unwrap(); + let timestamp = Utc.datetime_from_str(&captures[1], "%Y-%m-%d %H:%M").unwrap(); + + let event = match &captures[2] { + "falls asleep" => EventType::SLEEP, + "wakes up" => EventType::WAKE, + _ => EventType::SHIFT(captures[3].parse().unwrap()), + }; + + self.events.push(Event { + time: timestamp, + event, + }); + } + + self.events.sort_unstable(); + } + + fn get_sleeps(&self) -> HashMap { + let mut sleeps = HashMap::new(); + let mut guard: Option = None; + let mut sleep_start: Option> = None; + + + for event in &self.events { + match &event.event { + EventType::SHIFT(val) => { + guard = Some(*val); + sleep_start = None; + }, + EventType::SLEEP => { + sleep_start = Some(event.time.clone()); + }, + EventType::WAKE => { + let mut minutes = sleeps.entry(guard.unwrap()).or_insert([0u32;60]); + for m in sleep_start.unwrap().minute()..event.time.minute() { + minutes[m as usize] += 1; + } + } + } + } + + sleeps + } + + fn format_results(sleepers: HashMap, scores: HashMap) -> String { + let (best_sleeper, _) = scores.iter().max_by(|&(_, a), &(_, b)| a.cmp(b)).unwrap(); + + let best_minute = sleepers[best_sleeper].iter().enumerate() + .max_by(|&(_, a), &(_, b)| a.cmp(b)).unwrap().0; + + format!("{}", best_sleeper * (best_minute as usize)) + } +} + +impl common::Solution for Day04 { + fn part1(&mut self, input: &mut io::Read) -> String { + self.read_events(input); + let sleepers = self.get_sleeps(); + let scores: HashMap = sleepers.iter().map(|(k, v)| (*k, v.iter().sum())).collect(); + + Day04::format_results(sleepers, scores) + } + + fn part2(&mut self, input: &mut io::Read) -> String { + self.read_events(input); + let sleepers = self.get_sleeps(); + let scores: HashMap = sleepers.iter().map(|(k, v)| (*k, *v.iter().max().unwrap())).collect(); + + Day04::format_results(sleepers, scores) + } +} + +#[cfg(test)] +mod tests { + use common::Solution; + use day04::Day04; + + const SAMPLE_INPUT: &[u8] = b"[1518-11-01 00:00] Guard #10 begins shift +[1518-11-01 00:05] falls asleep +[1518-11-01 00:25] wakes up +[1518-11-01 00:30] falls asleep +[1518-11-01 00:55] wakes up +[1518-11-01 23:58] Guard #99 begins shift +[1518-11-02 00:40] falls asleep +[1518-11-02 00:50] wakes up +[1518-11-03 00:05] Guard #10 begins shift +[1518-11-03 00:24] falls asleep +[1518-11-03 00:29] wakes up +[1518-11-04 00:02] Guard #99 begins shift +[1518-11-04 00:36] falls asleep +[1518-11-04 00:46] wakes up +[1518-11-05 00:03] Guard #99 begins shift +[1518-11-05 00:45] falls asleep +[1518-11-05 00:55] wakes up"; + + #[test] + fn sample_part1() { + let mut instance = Day04::new(); + assert_eq!("240", instance.part1(&mut SAMPLE_INPUT)); + } + + #[test] + fn sample_part2() { + let mut instance = Day04::new(); + assert_eq!("4455", instance.part2(&mut SAMPLE_INPUT)); + } +} diff --git a/2018/src/main.rs b/2018/src/main.rs index 1c9e31c..7bf5547 100644 --- a/2018/src/main.rs +++ b/2018/src/main.rs @@ -1,4 +1,5 @@ extern crate clap; +extern crate chrono; extern crate regex; use clap::{Arg, App}; use std::fs; @@ -8,12 +9,14 @@ pub mod common; pub mod day01; pub mod day02; pub mod day03; +pub mod day04; fn get_impl(day: &str) -> Box { match day.parse() { Ok(1) => Box::new(day01::Day01::new()), Ok(2) => Box::new(day02::Day02::new()), Ok(3) => Box::new(day03::Day03::new()), + Ok(4) => Box::new(day04::Day04::new()), Ok(val) => panic!("Unimplemented day {}", val), _ => panic!("Invalid number"), } @@ -59,7 +62,7 @@ mod tests { #[test] fn test_get_impl() { // Verify that we can load all days - let last_implemented = 2; + let last_implemented = 4; for d in 1..=last_implemented { get_impl(&format!("{}", d)); }