153 lines
4.6 KiB
Rust
153 lines
4.6 KiB
Rust
use std::io::BufRead;
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
type Passport = HashMap<String, String>;
|
|
|
|
|
|
const ALLOWED_FIELDS: [&str; 8] = ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid", "cid"];
|
|
const OPTIONAL_FIELDS: [&str; 1] = ["cid"];
|
|
|
|
fn validate_fields((name, value): (&String, &String)) -> bool {
|
|
match name.as_str() {
|
|
"byr" => value.parse::<u32>().map(|byr| byr >= 1920 && byr <= 2002).unwrap_or(false),
|
|
"iyr" => value.parse::<u32>().map(|byr| byr >= 2010 && byr <= 2020).unwrap_or(false),
|
|
"eyr" => value.parse::<u32>().map(|byr| byr >= 2020 && byr <= 2030).unwrap_or(false),
|
|
"hgt" => {
|
|
let mut hgt_string = value.to_string();
|
|
let unit = hgt_string.split_off(hgt_string.len() - 2);
|
|
|
|
if let Ok(hgt) = hgt_string.parse::<u32>() {
|
|
match unit.as_str() {
|
|
"cm" => hgt >= 150 && hgt <= 193,
|
|
"in" => hgt >= 59 && hgt <= 76,
|
|
_ => false
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
},
|
|
"hcl" => {
|
|
let mut it = value.chars();
|
|
if let Some('#') = it.next() {
|
|
it.all(|c| c.is_ascii_hexdigit())
|
|
} else {
|
|
false
|
|
}
|
|
},
|
|
"ecl" => match value.as_str() {
|
|
"amb" | "blu" | "brn" | "gry" | "grn" | "hzl" | "oth" => true,
|
|
_ => false
|
|
},
|
|
"pid" => value.len() == 9 && value.chars().all(|c| c.is_ascii_digit()),
|
|
"cid" => true,
|
|
_ => unreachable!()
|
|
}
|
|
}
|
|
|
|
|
|
fn validate_passport_field(allowed_fields: &HashSet<String>, optional_fields: &HashSet<String>, passport: &Passport) -> bool {
|
|
let passport_fields: HashSet<_> = passport.keys().map(String::to_owned).collect();
|
|
optional_fields.is_superset(
|
|
&passport_fields
|
|
.symmetric_difference(&allowed_fields)
|
|
.map(String::to_owned)
|
|
.collect()
|
|
)
|
|
}
|
|
|
|
fn validate_passport_value(passport: &Passport) -> bool {
|
|
passport.iter().all(validate_fields)
|
|
}
|
|
|
|
fn parse_input<F: BufRead> (input: F) -> Vec<Passport> {
|
|
let tokens: Vec<_> = input.lines()
|
|
.map(|line| line.unwrap().split_whitespace().map(|v| Some(str::to_owned(v))).collect::<Vec<_>>())
|
|
.flat_map(|splited| if splited.len() == 0 {
|
|
vec![None]
|
|
} else {
|
|
splited
|
|
})
|
|
.collect();
|
|
|
|
tokens.split(Option::is_none).map(|passport|
|
|
passport.iter().map(|entry| {
|
|
let fields: Vec<_> = entry.as_ref().unwrap().split(':').collect();
|
|
(fields[0].to_string(), fields[1].to_string())
|
|
}).collect::<Passport>()
|
|
).collect()
|
|
}
|
|
|
|
fn to_string_set(list: &[&str]) -> HashSet<String> {
|
|
list.into_iter()
|
|
.map(|s| String::from(*s))
|
|
.collect()
|
|
}
|
|
|
|
pub fn validate_part2<F: BufRead> (input: F) -> usize {
|
|
let allowed_fields_list = to_string_set(&ALLOWED_FIELDS);
|
|
let optional_fields_list = to_string_set(&OPTIONAL_FIELDS);
|
|
parse_input(input)
|
|
.iter()
|
|
.filter(|passport| validate_passport_field(&allowed_fields_list, &optional_fields_list, passport))
|
|
.filter(|passport| validate_passport_value(passport))
|
|
.count()
|
|
}
|
|
|
|
pub fn validate_part1<F: BufRead> (input: F) -> usize {
|
|
let allowed_fileds_list = to_string_set(&ALLOWED_FIELDS);
|
|
let optional_fields_list = to_string_set(&OPTIONAL_FIELDS);
|
|
parse_input(input)
|
|
.iter()
|
|
.filter(|passport|
|
|
validate_passport_field(&allowed_fileds_list, &optional_fields_list, passport)
|
|
).count()
|
|
}
|
|
|
|
pub fn part1<F: BufRead> (input: F) {
|
|
println!("{:?}", validate_part1(input));
|
|
}
|
|
|
|
pub fn part2<F: BufRead> (input: F) {
|
|
println!("{:?}", validate_part2(input));
|
|
}
|
|
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
#[test]
|
|
pub fn test_invalid() {
|
|
let input = r#"eyr:1972 cid:100
|
|
hcl:#18171d ecl:amb hgt:170 pid:186cm iyr:2018 byr:1926
|
|
|
|
iyr:2019
|
|
hcl:#602927 eyr:1967 hgt:170cm
|
|
ecl:grn pid:012533040 byr:1946
|
|
|
|
hcl:dab227 iyr:2012
|
|
ecl:brn hgt:182cm pid:021572410 eyr:2020 byr:1992 cid:277
|
|
|
|
hgt:59cm ecl:zzz
|
|
eyr:2038 hcl:74454a iyr:2023
|
|
pid:3556412378 byr:2007"#.as_bytes();
|
|
assert_eq!(0, validate_part2(input));
|
|
}
|
|
|
|
#[test]
|
|
pub fn test_valid() {
|
|
let input = r#"pid:087499704 hgt:74in ecl:grn iyr:2012 eyr:2030 byr:1980
|
|
hcl:#623a2f
|
|
|
|
eyr:2029 ecl:blu cid:129 byr:1989
|
|
iyr:2014 pid:896056539 hcl:#a97842 hgt:165cm
|
|
|
|
hcl:#888785
|
|
hgt:164cm byr:2001 iyr:2015 cid:88
|
|
pid:545766238 ecl:hzl
|
|
eyr:2022
|
|
|
|
iyr:2010 hgt:158cm hcl:#b6652a ecl:blu byr:1944 eyr:2021 pid:093154719"#.as_bytes();
|
|
assert_eq!(4, validate_part2(input));
|
|
}
|
|
}
|