AoC_2020/src/day04.rs

169 lines
4.9 KiB
Rust

use aoc_runner_derive::{aoc, aoc_generator};
use std::collections::HashMap;
#[derive(Debug, PartialEq, Eq, Default)]
pub struct Creds {
byr: Option<String>,
iyr: Option<String>,
eyr: Option<String>,
hgt: Option<String>,
hcl: Option<String>,
ecl: Option<String>,
pid: Option<String>,
cid: Option<String>,
}
impl Creds {
fn fields(&self) -> HashMap<&str, Option<String>> {
let mut ret = HashMap::new();
ret.insert("byr", self.byr.clone());
ret.insert("iyr", self.iyr.clone());
ret.insert("eyr", self.eyr.clone());
ret.insert("hgt", self.hgt.clone());
ret.insert("hcl", self.hcl.clone());
ret.insert("ecl", self.ecl.clone());
ret.insert("pid", self.pid.clone());
ret.insert("cid", self.cid.clone());
ret
}
fn is_valid_part1(&self) -> bool {
for (k, v) in self.fields() {
if k == "cid" {
continue;
}
if v.is_none() {
return false;
}
}
return true;
}
fn year_in_range(val: &str, min: i64, max: i64) -> bool {
if val.len() != 4 {
return false;
}
if let Ok(val) = val.parse::<i64>() {
return val >= min && val <= max;
};
return false;
}
fn is_number(val: &str, ndigits: usize) -> bool {
val.chars().filter(|c| c.is_ascii_digit()).count() == ndigits
}
fn is_valid_height(val: &str) -> bool {
let it = val.chars();
let (num, unit): (String, String) = it.partition(|c| c.is_ascii_digit());
let num: Result<usize, _> = num.parse();
if let Ok(num) = num {
if unit == "cm" {
return (150..=193).contains(&num);
};
if unit == "in" {
return (59..=76).contains(&num);
};
}
return false;
}
fn validate_ecl(val: &str) -> bool {
let choices: Vec<&str> = vec!["amb", "blu", "brn", "gry", "grn", "hzl", "oth"];
return choices.iter().any(|&v| v == val);
}
fn validate_hcl(val: &str) -> bool {
if val.len() != 7 {
return false;
}
let mut ch = val.chars();
if ch.next() != Some('#') {
return false;
}
return ch.take_while(|v| v.is_ascii_hexdigit()).count() == 6;
}
fn validate(key: &str, value: &str) -> bool {
match key {
"byr" => Self::year_in_range(value, 1920, 2002),
"iyr" => Self::year_in_range(value, 2010, 2020),
"eyr" => Self::year_in_range(value, 2020, 2030),
"hgt" => Self::is_valid_height(value),
"pid" => Self::is_number(value, 9),
"ecl" => Self::validate_ecl(value),
"hcl" => Self::validate_hcl(value),
_ => false,
}
}
fn is_valid_part2(&self) -> bool {
for (k, v) in self.fields() {
if k == "cid" {
continue;
}
if let Some(v) = v {
if !Self::validate(k, &v) {
return false;
}
} else {
return false;
}
}
return true;
}
}
#[aoc_generator(day4)]
pub fn input_generator(input: &str) -> Vec<Creds> {
let mut ret = vec![];
for creds in input.split("\n\n") {
let creds = creds.replace("\n", " ");
let creds = creds.split(' ');
let mut cred_data = Creds::default();
for cred in creds {
let cred: Vec<&str> = cred.split(':').collect();
let (key, value) = (cred[0], cred[1]);
match key {
"byr" => {
cred_data.byr = Some(value.to_string());
}
"iyr" => {
cred_data.iyr = Some(value.to_string());
}
"eyr" => {
cred_data.eyr = Some(value.to_string());
}
"hgt" => {
cred_data.hgt = Some(value.to_string());
}
"hcl" => {
cred_data.hcl = Some(value.to_string());
}
"ecl" => {
cred_data.ecl = Some(value.to_string());
}
"pid" => {
cred_data.pid = Some(value.to_string());
}
"cid" => {
cred_data.cid = Some(value.to_string());
}
other => {
panic!("Invalid key: {}", other);
}
}
}
ret.push(cred_data);
}
ret
}
#[aoc(day4, part1)]
pub fn solve_part1(input: &[Creds]) -> usize {
return input.iter().filter(|v| v.is_valid_part1()).count();
}
#[aoc(day4, part2)]
pub fn solve_part2(input: &[Creds]) -> usize {
return input.iter().filter(|v| v.is_valid_part2()).count();
}