From efd70672fb0bc5cc29672b480401cc7be2466c57 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Fri, 4 Dec 2020 15:59:15 -0500 Subject: day 4 --- src/2020/4/mod.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/2020/mod.rs | 4 ++ src/util.rs | 11 ++++- 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/2020/4/mod.rs (limited to 'src') diff --git a/src/2020/4/mod.rs b/src/2020/4/mod.rs new file mode 100644 index 0000000..4b51b29 --- /dev/null +++ b/src/2020/4/mod.rs @@ -0,0 +1,132 @@ +use anyhow::Context as _; + +const REQUIRED_KEYS: &[&str] = + &["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]; + +pub fn part1() -> anyhow::Result<()> { + let batch = crate::util::read_file_str("data/4.txt")?; + let mut valid = 0; + for passport in parse(&batch)? { + let mut cur_valid = true; + for key in REQUIRED_KEYS { + if !passport.contains_key(&key.to_string()) { + cur_valid = false; + break; + } + } + if cur_valid { + valid += 1; + } + } + println!("{}", valid); + Ok(()) +} + +pub fn part2() -> anyhow::Result<()> { + let batch = crate::util::read_file_str("data/4.txt")?; + let mut valid = 0; + for passport in parse(&batch)? { + let mut cur_valid = true; + for key in REQUIRED_KEYS { + match passport.get(&key.to_string()) { + Some(val) => { + if !validate(key, val)? { + cur_valid = false; + break; + } + } + None => { + cur_valid = false; + break; + } + } + } + if cur_valid { + valid += 1; + } + } + println!("{}", valid); + Ok(()) +} + +fn parse( + batch: &str, +) -> anyhow::Result>> { + let mut res = vec![]; + let mut cur = std::collections::HashMap::new(); + for line in batch.lines() { + if line.is_empty() { + res.push(cur); + cur = std::collections::HashMap::new(); + continue; + } + + for field in line.split(' ') { + let mut parts = field.split(':'); + let key = parts.next().with_context(|| { + format!("failed to parse field '{}'", field) + })?; + let value = parts.next().with_context(|| { + format!("failed to parse field '{}'", field) + })?; + cur.insert(key.to_string(), value.to_string()); + } + } + if !cur.is_empty() { + res.push(cur); + } + Ok(res) +} + +fn validate(key: &str, val: &str) -> anyhow::Result { + match key { + "byr" => match val.parse::() { + Ok(year) => Ok(year >= 1920 && year <= 2002), + Err(_) => Ok(false), + }, + "iyr" => match val.parse::() { + Ok(year) => Ok(year >= 2010 && year <= 2020), + Err(_) => Ok(false), + }, + "eyr" => match val.parse::() { + Ok(year) => Ok(year >= 2020 && year <= 2030), + Err(_) => Ok(false), + }, + "hgt" => { + if val.len() < 3 { + Ok(false) + } else if val.ends_with("in") { + match val[0..val.len() - 2].parse::() { + Ok(inches) => Ok(inches >= 59 && inches <= 76), + Err(_) => Ok(false), + } + } else if val.ends_with("cm") { + match val[0..val.len() - 2].parse::() { + Ok(inches) => Ok(inches >= 150 && inches <= 193), + Err(_) => Ok(false), + } + } else { + Ok(false) + } + } + "hcl" => Ok(val.len() == 7 + && val.starts_with('#') + && val[1..] + == val[1..] + .matches(|c: char| c.is_ascii_hexdigit()) + .collect::()), + "ecl" => Ok(val == "amb" + || val == "blu" + || val == "brn" + || val == "gry" + || val == "grn" + || val == "hzl" + || val == "oth"), + "pid" => Ok(val.len() == 9 + && val + == val + .matches(|c: char| c.is_ascii_digit()) + .collect::()), + _ => Err(anyhow::anyhow!("invalid key found: {}", key)), + } +} diff --git a/src/2020/mod.rs b/src/2020/mod.rs index ff08a7e..812a379 100644 --- a/src/2020/mod.rs +++ b/src/2020/mod.rs @@ -4,6 +4,8 @@ mod day1; mod day2; #[path = "3/mod.rs"] mod day3; +#[path = "4/mod.rs"] +mod day4; pub fn run(day: u8, puzzle: u8) -> anyhow::Result<()> { match (day, puzzle) { @@ -13,6 +15,8 @@ pub fn run(day: u8, puzzle: u8) -> anyhow::Result<()> { (2, 2) => day2::part2(), (3, 1) => day3::part1(), (3, 2) => day3::part2(), + (4, 1) => day4::part1(), + (4, 2) => day4::part2(), _ => Err(anyhow::anyhow!("unknown puzzle {}-{}", day, puzzle)), } } diff --git a/src/util.rs b/src/util.rs index fdc3bed..94f9796 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,5 @@ use anyhow::Context as _; -use std::io::BufRead as _; +use std::io::{BufRead as _, Read as _}; pub fn read_ints(filename: &str) -> anyhow::Result> { let f = std::fs::File::open(filename) @@ -15,3 +15,12 @@ pub fn read_ints(filename: &str) -> anyhow::Result> { .collect(); ints } + +pub fn read_file_str(filename: &str) -> anyhow::Result { + let mut f = std::fs::File::open(filename) + .with_context(|| format!("couldn't find data file {}", filename))?; + let mut s = String::new(); + f.read_to_string(&mut s) + .context("failed to read map contents")?; + Ok(s) +} -- cgit v1.2.3-54-g00ecf