2020-12-04 11:43:17 +01:00
|
|
|
module day4.part2.main;
|
|
|
|
|
|
|
|
import std;
|
|
|
|
|
|
|
|
void main()
|
|
|
|
{
|
|
|
|
readText("input").parsePassports.countValidPassports.writeln;
|
|
|
|
}
|
|
|
|
|
|
|
|
alias Passport = string[string];
|
|
|
|
|
|
|
|
auto parsePassports(string input)
|
|
|
|
{
|
|
|
|
return input.splitter("\n\n").map!(passport => passport.splitter().map!((entry) {
|
|
|
|
auto keyAndValue = entry.split(':');
|
|
|
|
return tuple(keyAndValue[0], keyAndValue[1]);
|
|
|
|
}).assocArray);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isValidHeight(string hgt)
|
|
|
|
{
|
|
|
|
auto match = hgt.matchFirst(r"^(\d+)(cm|in)$");
|
|
|
|
if (!match || match.length != 3)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto number = match[1].to!int;
|
|
|
|
switch (match[2])
|
|
|
|
{
|
|
|
|
case "cm":
|
|
|
|
return number >= 150 && number <= 193;
|
|
|
|
break;
|
|
|
|
case "in":
|
|
|
|
return number >= 59 && number <= 76;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-04 12:32:58 +01:00
|
|
|
alias isBetween = (x, min, max) => x >= min && x <= max;
|
2020-12-04 11:43:17 +01:00
|
|
|
|
2020-12-04 12:32:58 +01:00
|
|
|
bool isValidPassport(Passport passport)
|
2020-12-04 11:43:17 +01:00
|
|
|
{
|
2020-12-04 12:32:58 +01:00
|
|
|
// dfmt off
|
|
|
|
auto validators = [
|
|
|
|
"byr" : s => s.length == 4 && s.all!isDigit && s.to!int.isBetween(1920, 2002),
|
|
|
|
"iyr" : s => s.length == 4 && s.all!isDigit && s.to!int.isBetween(2010, 2020),
|
|
|
|
"eyr" : s => s.length == 4 && s.all!isDigit && s.to!int.isBetween(2020, 2030),
|
|
|
|
"hgt" : &isValidHeight,
|
|
|
|
"hcl" : s => s.matchFirst(r"^#[0-9a-f]{6}$").to!bool,
|
|
|
|
"ecl" : s => [ "amb", "blu", "brn", "gry", "grn", "hzl", "oth" ].canFind(s),
|
|
|
|
"pid" : s => s.matchFirst(r"^\d{9}$").to!bool
|
2020-12-04 11:43:17 +01:00
|
|
|
];
|
2020-12-04 12:32:58 +01:00
|
|
|
// dfmt on
|
2020-12-04 11:43:17 +01:00
|
|
|
|
2020-12-04 12:32:58 +01:00
|
|
|
return validators.byKeyValue.all!(validator => validator.key in passport
|
|
|
|
&& validator.value()(passport[validator.key]));
|
2020-12-04 11:43:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
auto countValidPassports(R)(R r) if (isInputRange!R && is(ElementType!R == Passport))
|
|
|
|
{
|
|
|
|
return r.filter!isValidPassport.count;
|
|
|
|
}
|
|
|
|
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
auto input = `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`;
|
|
|
|
|
|
|
|
auto passPorts = input.parsePassports;
|
|
|
|
assert(passPorts.countValidPassports == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
auto input = `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`;
|
|
|
|
|
|
|
|
auto passPorts = input.parsePassports;
|
|
|
|
assert(passPorts.countValidPassports == 4);
|
|
|
|
}
|