Day 4 part 1

This commit is contained in:
Tyler Hallada 2018-12-16 15:59:17 -05:00
parent ac311a1b1c
commit dc1211cae2
3 changed files with 1186 additions and 12 deletions

1092
inputs/4.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@ use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::fmt; use std::fmt;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::iter::FromIterator; use std::iter::FromIterator;
use chrono::prelude::*; use chrono::prelude::*;
@ -27,6 +28,16 @@ enum Record {
}, },
} }
impl Record {
fn time(&self) -> NaiveDateTime {
match *self {
Record::Start { time, guard_id: _ } => time,
Record::Sleep { time } => time,
Record::Wake { time } => time,
}
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
struct MalformedRecord { struct MalformedRecord {
details: String details: String
@ -50,35 +61,78 @@ impl Error for MalformedRecord {
} }
} }
pub fn solve_part1() -> Result<u32, Box<Error>> {
Ok(get_part1(INPUT)?)
}
fn get_part1(filename: &str) -> Result<u32, Box<Error>> {
let records = read_records(filename)?;
let minutes_asleep = minutes_asleep_per_guard(records);
let sleepiest_guard = minutes_asleep.iter().max_by_key(|&(_, mins)| mins.len()).unwrap();
let sleepiest_minute = mode(sleepiest_guard.1);
Ok(sleepiest_guard.0 * sleepiest_minute)
}
fn mode(numbers: &[u32]) -> u32 {
let mut occurences = HashMap::new();
for &value in numbers {
*occurences.entry(value).or_insert(0) += 1;
}
occurences
.into_iter()
.max_by_key(|&(_, count)| count)
.map(|(val, _)| val)
.unwrap_or(0)
}
fn minutes_asleep_per_guard(mut records: Vec<Record>) -> HashMap<u32, Vec<u32>> {
let mut minutes_asleep: HashMap<u32, Vec<u32>> = HashMap::new();
records.sort_by_key(|r| r.time());
let mut current_guard = 0;
let mut fell_asleep = 0;
for record in records {
match record {
Record::Start { time: _, guard_id } => current_guard = guard_id,
Record::Sleep { time } => fell_asleep = time.minute(),
Record::Wake { time } => {
let mut slept_minutes = (fell_asleep..time.minute()).collect();
match minutes_asleep.entry(current_guard) {
Entry::Vacant(e) => { e.insert(slept_minutes); },
Entry::Occupied(mut e) => { e.get_mut().append(&mut slept_minutes); },
}
}
}
}
minutes_asleep
}
fn read_records(filename: &str) -> Result<Vec<Record>, Box<Error>> { fn read_records(filename: &str) -> Result<Vec<Record>, Box<Error>> {
let mut records: Vec<Record> = Vec::new(); let mut records: Vec<Record> = Vec::new();
let record_regex = let record_regex =
Regex::new(r"\[(?P<timestamp>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2})\]\s(?:(?P<start>Guard #(?P<guard_id>\d+) begins shift)|(?P<sleep>falls asleep)|(?P<wake>wakes up))")?; Regex::new(concat!(
r"\[(?P<timestamp>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2})\]\s(?:",
r"(?P<start>Guard #(?P<guard_id>\d+) begins shift)|",
r"(?P<sleep>falls asleep)|",
r"(?P<wake>wakes up))"))?;
let file = File::open(filename)?; let file = File::open(filename)?;
for line in BufReader::new(file).lines() { for line in BufReader::new(file).lines() {
match record_regex.captures(&line?) { match record_regex.captures(&line?) {
Some(captures) => { Some(captures) => {
println!("{:?}", NaiveDateTime::parse_from_str( let time = NaiveDateTime::parse_from_str(
&get_captured_field(&captures, "timestamp")?, &get_captured_field(&captures, "timestamp")?,
"%Y-%m-%d %H:%M")?); "%Y-%m-%d %H:%M")?;
if has_captured_field(&captures, "start")? { if has_captured_field(&captures, "start")? {
records.push(Record::Start { records.push(Record::Start {
time: NaiveDateTime::parse_from_str( time: time,
&get_captured_field(&captures, "timestamp")?,
"%Y-%m-%d %H:%M")?,
guard_id: get_captured_field(&captures, "guard_id")?.parse()?, guard_id: get_captured_field(&captures, "guard_id")?.parse()?,
}); });
} else if has_captured_field(&captures, "sleep")? { } else if has_captured_field(&captures, "sleep")? {
records.push(Record::Sleep { records.push(Record::Sleep {
time: NaiveDateTime::parse_from_str( time: time,
&get_captured_field(&captures, "timestamp")?,
"%Y-%m-%d %H:%M")?,
}); });
} else { } else {
records.push(Record::Wake { records.push(Record::Wake {
time: NaiveDateTime::parse_from_str( time: time,
&get_captured_field(&captures, "timestamp")?,
"%Y-%m-%d %H:%M")?,
}); });
} }
}, },
@ -203,4 +257,30 @@ mod tests {
), ),
} }
} }
#[test]
fn gets_minutes_asleep_per_guard() {
let mut expected: HashMap<u32, Vec<u32>> = HashMap::new();
expected.insert(10, vec![5, 6, 7, 8, 9]);
assert_eq!(minutes_asleep_per_guard(vec![
Record::Sleep {
time: NaiveDateTime::parse_from_str(
"1518-11-01 00:05", "%Y-%m-%d %H:%M").unwrap(),
},
Record::Start {
time: NaiveDateTime::parse_from_str(
"1518-11-01 00:00", "%Y-%m-%d %H:%M").unwrap(),
guard_id: 10,
},
Record::Wake {
time: NaiveDateTime::parse_from_str(
"1518-11-01 00:10", "%Y-%m-%d %H:%M").unwrap(),
},
]), expected);
}
#[test]
fn solves_part1() {
assert_eq!(get_part1(TEST_INPUT).unwrap(), 240);
}
} }

View File

@ -13,4 +13,6 @@ fn main() {
println!("Day 3:"); println!("Day 3:");
println!("{}", day3::solve_part1().unwrap()); println!("{}", day3::solve_part1().unwrap());
println!("{}", day3::solve_part2().unwrap().unwrap()); println!("{}", day3::solve_part2().unwrap().unwrap());
println!("Day 4:");
println!("{}", day4::solve_part1().unwrap());
} }