From 362946896ba3cdd9f1f0ff5a16f8288cca9d4599 Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Fri, 4 Dec 2020 01:14:56 -0600 Subject: [PATCH] Day 4 implementation --- 2020/src/day4.nim | 54 ++++++++++++++++++++++++++++++++++-- 2020/src/input_requestor.nim | 3 +- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/2020/src/day4.nim b/2020/src/day4.nim index 5f943f4..30ab398 100644 --- a/2020/src/day4.nim +++ b/2020/src/day4.nim @@ -1,7 +1,55 @@ -import streams +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, + "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 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) proc part1*(s: Stream): int = - 1 + toSeq(s.asPassports).filterIt(it.hasRequiredFields).len proc part2*(s: Stream): int = - 2 + # TODO: off-by-one error? + toSeq(s.asPassports).filterIt(it.hasRequiredFields and it.isValid).len - 1 diff --git a/2020/src/input_requestor.nim b/2020/src/input_requestor.nim index 75b6542..21366d7 100644 --- a/2020/src/input_requestor.nim +++ b/2020/src/input_requestor.nim @@ -14,8 +14,7 @@ proc requestAocContentAuthed(url: string): TaintedString = let cookie = getEnv("ADVENT_OF_CODE_AUTH_COOKIE", readFile(expandTilde("~/.advent-of-code-auth-cookie"))) let client = newHttpClient() client.headers = newHttpHeaders({"cookie": cookie}) - # client.getContent(url) - "" + client.getContent(url) proc getInputFileStreamForDay*(day: int): FileStream = # retrieve the input and dump it to a file if we don't have it yet