adventofcode/2020/Day04.cs

179 lines
4.8 KiB
C#

using System;
using static System.Console;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Xunit;
namespace AdventOfCode {
public class Day04 {
static string[] requiredFields = {
// "cid" not required because we are hackers
"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"
};
static string[] validEyeColors = {
"amb", "blu", "brn", "gry", "grn", "hzl", "oth"
};
static bool IsValidPassport1(string input) {
string[] fields = input.Split(' ');
var fieldsPresent = new HashSet<string>();
foreach (string field in fields) {
string[] tokens = field.Split(':');
string key = tokens[0];
fieldsPresent.Add(key);
}
return fieldsPresent.IsSupersetOf(requiredFields);
}
static int ParseInt(string value) {
int result;
if (!int.TryParse(value, out result)) {
return -1;
}
return result;
}
static bool ValidateYear(string value, int min, int max) {
int year = ParseInt(value);
return year >= min && year <= max;
}
static bool ValidateHeight(string value) {
string unit = value.Substring(value.Length - 2);
int amount = ParseInt(value.Substring(0, value.Length - 2));
if (unit == "cm") {
return amount >= 150 && amount <= 193;
} else if (unit == "in") {
return amount >= 59 && amount <= 76;
} else { // not cm or in
return false;
}
}
static bool IsValidPassport2(string input) {
string[] fields = input.Split(' ');
var fieldsPresent = new HashSet<string>();
foreach (string field in fields) {
if (field.Length == 0) {
continue;
}
string[] tokens = field.Split(':');
string key = tokens[0];
string value = tokens[1];
bool valid = key switch {
"byr" => ValidateYear(value, 1920, 2002),
"iyr" => ValidateYear(value, 2010, 2020),
"eyr" => ValidateYear(value, 2020, 2030),
"hgt" => ValidateHeight(value),
"hcl" => Regex.IsMatch(value, @"^#[0-9a-f]{6}$"),
"ecl" => validEyeColors.Contains(value),
"pid" => Regex.IsMatch(value, @"^[0-9]{9}$"),
_ => false
};
if (valid) {
fieldsPresent.Add(key);
}
}
return fieldsPresent.IsSupersetOf(requiredFields);
}
static List<string> ParsePassports(string[] input) {
var result = new List<string>();
string passport = "";
foreach (string line in input) {
if (line == "") {
result.Add(passport);
passport = "";
} else {
passport += line + " ";
}
}
result.Add(passport);
return result;
}
static int CountValidPassports1(string[] input) {
return ParsePassports(input).Count(IsValidPassport1);
}
static int CountValidPassports2(string[] input) {
return ParsePassports(input).Count(IsValidPassport2);
}
static int Part1() {
string[] input = File.ReadAllLines(Util.RootDir + "day04.txt");
return CountValidPassports1(input);
}
static int Part2() {
string[] input = File.ReadAllLines(Util.RootDir + "day04.txt");
return CountValidPassports2(input);
}
[Fact]
public static void Test() {
string[] example =
@"ecl:gry pid:860033327 eyr:2020 hcl:#fffffd
byr:1937 iyr:2017 cid:147 hgt:183cm
iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884
hcl:#cfa07d byr:1929
hcl:#ae17e1 iyr:2013
eyr:2024
ecl:brn pid:760753108 byr:1931
hgt:179cm
hcl:#cfa07d eyr:2025 pid:166559648
iyr:2011 ecl:brn hgt:59in".Split('\n');
Assert.Equal(2, CountValidPassports1(example));
Assert.Equal(256, Part1());
string[] invalidPassports =
@"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".Split('\n');
string[] validPassports =
@"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".Split('\n');
List<string> invalid = ParsePassports(invalidPassports);
Assert.Equal(4, invalid.Count);
Assert.All(invalid, item => Assert.False(IsValidPassport2(item)));
List<string> valid = ParsePassports(validPassports);
Assert.Equal(4, valid.Count);
Assert.All(valid, item => Assert.True(IsValidPassport2(item)));
Assert.Equal(198, Part2());
}
}
}