diff --git a/2020/src/day4.nim b/2020/src/day4.nim index 1c0b39b..1a896c2 100644 --- a/2020/src/day4.nim +++ b/2020/src/day4.nim @@ -1,54 +1,28 @@ import streams, strutils, sets, sequtils, sugar, re, tables -proc matchingHeightRange(v: string): (int, int) = - if v.endsWith("cm"): return (150, 193) - elif v.endsWith("in"): return (59, 76) - -proc isBetween(x: int, r: (int, int)): bool = x >= r[0] and x <= r[1] -proc validBirthYear(y: int): bool = y >= 1920 and y <= 2002 -proc validIssuedYear(y: int): bool = y >= 2010 and y <= 2020 -proc validExpiryYear(y: int): bool = y >= 2020 and y <= 2030 - -let nonDecimal = re"[^0-9]" -let hgt = re"\d+(cm|in)" -let hcl = re"#[0-9a-f]{6}" -let ecl = re"(amb|blu|brn|gry|grn|hzl|oth)" -let pid = re"^\d{9}$" - -let validators = { - "hgt": (v: string) => - v.match(hgt) and - v.replace(nonDecimal).parseInt().isBetween(v.matchingHeightRange), - "hcl": (v: string) => v.match hcl, - "ecl": (v: string) => v.match ecl, - "pid": (v: string) => v.match pid, - "byr": (v: string) => validBirthYear parseInt v, - "iyr": (v: string) => validIssuedYear parseInt v, - "eyr": (v: string) => validExpiryYear parseInt v, +let validators = toTable({ + "hcl": (v: string) => v.match re"^#[0-9a-f]{6}$", + "ecl": (v: string) => v.match re"^(amb|blu|brn|gry|grn|hzl|oth)$", + "pid": (v: string) => v.match re"^\d{9}$", + "byr": (v: string) => parseInt(v) in 1920..2002, + "iyr": (v: string) => parseInt(v) in 2010..2020, + "eyr": (v: string) => parseInt(v) in 2020..2030, + "hgt": proc (v: string): bool = + let (mn, mx) = if v.endsWith("cm"): (150, 193) else: (59, 76) + v.match(re"^\d+(cm|in)$") and v.replace(re"[^0-9]").parseInt in mn..mx, "cid": (v: string) => true, -}.toTable +}) -proc passportTable(passport: string): Table[string, string] = - passport.split(' ').filterIt(it != "").mapIt(it.split(':', 2)).mapIt((it[0], it[1])).toTable - -iterator asPassports(s: Stream): Table[string, string] = - var p = "" - for l in s.lines: - p &= l & " " - if l == "" or s.atEnd: - yield p.passportTable - p = "" +proc parsePassports(s: Stream): seq[Table[string, string]] = + s.readAll().split("\n\n").mapIt(it.replace("\n", " ").split(' ') + .filterIt(it != "").mapIt(it.split(':', 2)).mapIt((it[0], it[1])).toTable) proc hasRequiredFields(p: Table[string, string]): bool = - let optionalFields = ["cid"].toHashSet() - toSeq(p.keys).toHashSet.difference(optionalFields) == - toSeq(validators.keys).toHashSet.difference(optionalFields) - -proc isValid(p: Table[string, string]): bool = - toSeq(p.pairs).mapIt(validators[it[0]](it[1])).foldl(a and b) + toSeq(validators.keys).toHashSet.difference(["cid"].toHashSet) <= toSeq(p.keys).toHashSet proc part1*(s: Stream): int = - toSeq(s.asPassports).filterIt(it.hasRequiredFields).len + s.parsePassports.filterIt(it.hasRequiredFields).len proc part2*(s: Stream): int = - toSeq(s.asPassports).filterIt(it.hasRequiredFields and it.isValid).len + s.parsePassports.filterIt(it.hasRequiredFields and + toSeq(it.pairs).mapIt(validators[it[0]](it[1])).foldl(a and b)).len