diff --git a/aoc2020/input/day04_provided.txt b/aoc2020/input/day04_provided1.txt similarity index 100% rename from aoc2020/input/day04_provided.txt rename to aoc2020/input/day04_provided1.txt diff --git a/aoc2020/input/day04_provided2.txt b/aoc2020/input/day04_provided2.txt new file mode 100644 index 0000000..a7fa5bb --- /dev/null +++ b/aoc2020/input/day04_provided2.txt @@ -0,0 +1,13 @@ +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 diff --git a/aoc2020/input/day04_provided3.txt b/aoc2020/input/day04_provided3.txt new file mode 100644 index 0000000..ca41800 --- /dev/null +++ b/aoc2020/input/day04_provided3.txt @@ -0,0 +1,12 @@ +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 diff --git a/aoc2020/src/day04.rs b/aoc2020/src/day04.rs index cdf05c0..85c3445 100644 --- a/aoc2020/src/day04.rs +++ b/aoc2020/src/day04.rs @@ -8,6 +8,7 @@ pub fn run() -> aoc::Result { let mut res = String::with_capacity(128); writeln!(res, "part 1: {}", part1(INPUT)?)?; + writeln!(res, "part 2: {}", part2(INPUT)?)?; Ok(res) } @@ -39,6 +40,12 @@ fn part1(input: &str) -> aoc::Result { Ok(passports.iter().filter(|p| p.has_valid_fields()).count()) } +fn part2(input: &str) -> aoc::Result { + let passports = get_passports(input)?; + + Ok(passports.iter().filter(|p| p.is_valid()).count()) +} + #[derive(Debug)] struct Passport { byr: Option, @@ -62,6 +69,115 @@ impl Passport { && self.ecl.is_some() && self.pid.is_some() } + + fn is_valid(&self) -> bool { + self.byr_valid() + && self.iyr_valid() + && self.eyr_valid() + && self.hgt_valid() + && self.hcl_valid() + && self.ecl_valid() + && self.pid_valid() + } + + fn byr_valid(&self) -> bool { + let byr = match &self.byr { + Some(s) => s, + None => return false, + }; + + if let Ok(res) = byr.parse::() { + res >= 1920 && res <= 2002 + } else { + false + } + } + + fn iyr_valid(&self) -> bool { + let iyr = match &self.iyr { + Some(s) => s, + None => return false, + }; + + if let Ok(res) = iyr.parse::() { + res >= 2010 && res <= 2020 + } else { + false + } + } + + fn eyr_valid(&self) -> bool { + let eyr = match &self.eyr { + Some(s) => s, + None => return false, + }; + + if let Ok(res) = eyr.parse::() { + res >= 2020 && res <= 2030 + } else { + false + } + } + + fn hgt_valid(&self) -> bool { + let hgt = match &self.hgt { + Some(s) => s, + None => return false, + }; + + if let Some(num) = hgt.strip_suffix("in") { + if let Ok(res) = num.parse::() { + res >= 59 && res <= 76 + } else { + false + } + } else if let Some(num) = hgt.strip_suffix("cm") { + if let Ok(res) = num.parse::() { + res >= 150 && res <= 193 + } else { + false + } + } else { + false + } + } + + fn hcl_valid(&self) -> bool { + let hcl = match &self.hcl { + Some(s) => s, + None => return false, + }; + + if let Some(rest) = hcl.strip_prefix("#") { + rest.chars().filter(|c| !c.is_ascii_hexdigit()).count() == 0 + } else { + false + } + } + + fn ecl_valid(&self) -> bool { + let ecl = match &self.ecl { + Some(s) => s, + None => return false, + }; + + ecl == "amb" + || ecl == "blu" + || ecl == "brn" + || ecl == "gry" + || ecl == "grn" + || ecl == "hzl" + || ecl == "oth" + } + + fn pid_valid(&self) -> bool { + let pid = match &self.pid { + Some(s) => s, + None => return false, + }; + + pid.chars().filter(|c| !c.is_ascii_digit()).count() == 0 && pid.len() == 9 + } } impl FromStr for Passport { @@ -97,15 +213,28 @@ impl FromStr for Passport { mod tests { use super::*; - static PROVIDED: &'static str = include_str!("../input/day04_provided.txt"); + static PROVIDED1: &'static str = include_str!("../input/day04_provided1.txt"); + static PROVIDED2: &'static str = include_str!("../input/day04_provided2.txt"); + static PROVIDED3: &'static str = include_str!("../input/day04_provided3.txt"); #[test] fn part1_provided() { - assert_eq!(part1(PROVIDED).unwrap(), 2); + assert_eq!(part1(PROVIDED1).unwrap(), 2); } #[test] fn part1_real() { assert_eq!(part1(INPUT).unwrap(), 192); } + + #[test] + fn part2_provided() { + assert_eq!(part2(PROVIDED2).unwrap(), 0); + assert_eq!(part2(PROVIDED3).unwrap(), 4); + } + + #[test] + fn part2_real() { + assert_eq!(part2(INPUT).unwrap(), 101); + } }