180 lines
4.5 KiB
Rust
180 lines
4.5 KiB
Rust
//! Day 04 of 2020.
|
|
|
|
use crate::prelude::*;
|
|
|
|
/// Get the solution for day 04 of 2020.
|
|
pub fn solution() -> Solution {
|
|
Solution::new(Day::new(4, 2020), part_1, part_2).with_expected(237, 172)
|
|
}
|
|
|
|
/// The passport puzzle input.
|
|
#[derive(Debug, Clone)]
|
|
struct Passport {
|
|
/// Birth year.
|
|
byr: Option<String>,
|
|
/// Issue year.
|
|
iyr: Option<String>,
|
|
/// Expiration year.
|
|
eyr: Option<String>,
|
|
/// Height.
|
|
hgt: Option<String>,
|
|
/// Hair color.
|
|
hcl: Option<String>,
|
|
/// Eye color.
|
|
ecl: Option<String>,
|
|
/// Passport ID.
|
|
pid: Option<String>,
|
|
/// Country ID.
|
|
cid: Option<String>,
|
|
}
|
|
|
|
/// Parse the puzzle input into a list of passports.
|
|
fn parse_passports(input: &str) -> Vec<Passport> {
|
|
let mut passports = vec![];
|
|
let blank = Passport {
|
|
byr: None,
|
|
iyr: None,
|
|
eyr: None,
|
|
hgt: None,
|
|
hcl: None,
|
|
ecl: None,
|
|
pid: None,
|
|
cid: None,
|
|
};
|
|
let mut intermediate = blank.clone();
|
|
|
|
for line in input.lines() {
|
|
if line.is_empty() {
|
|
passports.push(intermediate.clone());
|
|
intermediate = blank.clone();
|
|
continue;
|
|
}
|
|
|
|
let components = line.split(' ');
|
|
for component in components {
|
|
let colon_index = component.find(':').unwrap();
|
|
let key = &component[0..colon_index];
|
|
let value = &component[colon_index + 1..];
|
|
match key {
|
|
"byr" => intermediate.byr = Some(value.to_string()),
|
|
"iyr" => intermediate.iyr = Some(value.to_string()),
|
|
"eyr" => intermediate.eyr = Some(value.to_string()),
|
|
"hgt" => intermediate.hgt = Some(value.to_string()),
|
|
"hcl" => intermediate.hcl = Some(value.to_string()),
|
|
"ecl" => intermediate.ecl = Some(value.to_string()),
|
|
"pid" => intermediate.pid = Some(value.to_string()),
|
|
"cid" => intermediate.cid = Some(value.to_string()),
|
|
_ => unreachable!("{key}"),
|
|
};
|
|
}
|
|
}
|
|
|
|
passports
|
|
}
|
|
|
|
/// The logic to solve part one.
|
|
fn part_1(input: &str) -> Result<String> {
|
|
Ok(
|
|
parse_passports(input)
|
|
.into_iter()
|
|
.filter(|passport| {
|
|
passport.byr.is_some()
|
|
&& passport.iyr.is_some()
|
|
&& passport.eyr.is_some()
|
|
&& passport.hgt.is_some()
|
|
&& passport.hcl.is_some()
|
|
&& passport.ecl.is_some()
|
|
&& passport.pid.is_some()
|
|
})
|
|
.count()
|
|
.to_string(),
|
|
)
|
|
}
|
|
|
|
/// The logic to solve part two.
|
|
fn part_2(input: &str) -> Result<String> {
|
|
let hcl_regex = Regex::new("^#[a-fA-F0-9]{6}$").unwrap();
|
|
let pid_regex = Regex::new("^[0-9]{9}$").unwrap();
|
|
|
|
let ecl_values = vec![
|
|
"amb".to_string(),
|
|
"blu".to_string(),
|
|
"brn".to_string(),
|
|
"gry".to_string(),
|
|
"grn".to_string(),
|
|
"hzl".to_string(),
|
|
"oth".to_string(),
|
|
];
|
|
|
|
Ok(
|
|
parse_passports(input)
|
|
.into_iter()
|
|
.filter(|passport| {
|
|
passport.byr.is_some()
|
|
&& passport.iyr.is_some()
|
|
&& passport.eyr.is_some()
|
|
&& passport.hgt.is_some()
|
|
&& passport.hcl.is_some()
|
|
&& passport.ecl.is_some()
|
|
&& passport.pid.is_some()
|
|
})
|
|
.filter(|passport| {
|
|
let byr = passport
|
|
.byr
|
|
.as_ref()
|
|
.unwrap()
|
|
.parse::<i32>()
|
|
.unwrap_or_default();
|
|
(1920..=2002).contains(&byr)
|
|
})
|
|
.filter(|passport| {
|
|
let iyr = passport
|
|
.iyr
|
|
.as_ref()
|
|
.unwrap()
|
|
.parse::<i32>()
|
|
.unwrap_or_default();
|
|
(2010..=2020).contains(&iyr)
|
|
})
|
|
.filter(|passport| {
|
|
let eyr = passport
|
|
.eyr
|
|
.as_ref()
|
|
.unwrap()
|
|
.parse::<i32>()
|
|
.unwrap_or_default();
|
|
(2020..=2030).contains(&eyr)
|
|
})
|
|
.filter(|passport| {
|
|
let hgt = passport.hgt.as_ref().unwrap();
|
|
match &hgt[hgt.len() - 2..] {
|
|
"cm" => {
|
|
let value =
|
|
hgt[0..hgt.len() - 2].parse::<i32>().unwrap_or_default();
|
|
(150..=193).contains(&value)
|
|
}
|
|
"in" => {
|
|
let value =
|
|
hgt[0..hgt.len() - 2].parse::<i32>().unwrap_or_default();
|
|
(59..=76).contains(&value)
|
|
}
|
|
_ => false,
|
|
}
|
|
})
|
|
.filter(|passport| {
|
|
let hcl = passport.hcl.as_ref().unwrap();
|
|
hcl_regex.is_match(hcl)
|
|
})
|
|
.filter(|passport| {
|
|
let ecl = passport.ecl.as_ref().unwrap();
|
|
ecl_values.contains(ecl)
|
|
})
|
|
.filter(|passport| {
|
|
let pid = passport.pid.as_ref().unwrap();
|
|
pid_regex.is_match(pid)
|
|
})
|
|
.count()
|
|
.to_string(),
|
|
)
|
|
}
|