diff --git a/2020/src/day1.nim b/2020/src/day1.nim index 7f2668e..00a880d 100644 --- a/2020/src/day1.nim +++ b/2020/src/day1.nim @@ -1,29 +1,19 @@ -import sets, streams, strutils, input_helpers +import sets, streams, input_helpers, sequtils, options -# Day 1 +let targetSum = 2020 -let day1TargetSum = 2020 +proc findComplement(nums: seq[int], complement = targetSum): Option[(int, int)] = + var targets = initHashSet[int]() + for n in nums: + if targets.contains(complement - n): return some(((complement - n), n)) + else: targets.incl n proc part1*(s: Stream): int = - var targets = initHashSet[int]() - for n in asInts(s): - if targets.contains(day1TargetSum - n): - return (day1TargetSum - n) * n - else: - targets.incl n + let (n1, n2) = findComplement(toSeq(asInts(s))).get; n1 * n2 -proc part2*(strm: Stream): int = - # this works exactly the same as the previous algorithm, except we simply - # permute once more - # TODO: if I was really cool, I could split the shared functionality into - # a shared proc - var nums: seq[int] - for line in strm.lines(): - nums.add parseInt line +proc part2*(s: Stream): int = + let nums = toSeq(asInts(s)) for n in nums: - var iset = initHashSet[int]() - let nTargetSum = day1TargetSum - n - for n2 in nums: - if iset.contains(nTargetSum - n2): - return n * n2 * (nTargetSum - n2) - iset.incl n2 + let comp = findComplement(nums, targetSum - n) + if comp.isSome: + let (n1, n2) = comp.get; return n * n1 * n2 diff --git a/2020/src/day2.nim b/2020/src/day2.nim index f0d82f9..d62ae7d 100644 --- a/2020/src/day2.nim +++ b/2020/src/day2.nim @@ -2,6 +2,15 @@ import streams, strutils, re type PasswordPolicy = tuple[min: int, max: int, keyChar: char] +let parsePasswordLineRe = re"^(\d+)-(\d+) (.): (.*)$" +proc parsePasswordLine(str: string): (PasswordPolicy, string) = + var matches: array[4, string] + if match(str, parsePasswordLineRe, matches): + return ((min: parseInt(matches[0]), max: parseInt(matches[1]), keyChar: matches[2][0]), matches[3]) + +iterator asPasswordPolicies(s: Stream): (PasswordPolicy, string) = + for line in s.lines(): yield parsePasswordLine line + proc isValidPassword(str: string, pp: PasswordPolicy): bool = let count = str.count pp.keyChar (pp.min <= count) and (count <= pp.max) @@ -9,22 +18,10 @@ proc isValidPassword(str: string, pp: PasswordPolicy): bool = proc isValidPasswordPart2(str: string, pp: PasswordPolicy): bool = (str[pp.min - 1] == pp.keyChar) xor (pp.keyChar == str[pp.max - 1]) -let parsePasswordPolicyRe = re"^(\d+)-(\d+) (.): (.*)$" -proc parsePasswordPolicy(str: string): (PasswordPolicy, string) = - var matches: array[4, string] - if match(str, parsePasswordPolicyRe, matches): - return ((min: parseInt(matches[0]), max: parseInt(matches[1]), keyChar: matches[2][0]), matches[3]) - -iterator asPasswordPolicies(s: Stream): (PasswordPolicy, string) = - for line in s.lines(): - yield parsePasswordPolicy line - proc part1*(s: Stream): int = - for (pp, pw) in asPasswordPolicies(s): - if isValidPassword(pw, pp): - result += 1 + for (pp, pw) in s.asPasswordPolicies: + if isValidPassword(pw, pp): inc result proc part2*(s: Stream): int = - for (pp, pw) in asPasswordPolicies(s): - if isValidPasswordPart2(pw, pp): - result += 1 + for (pp, pw) in s.asPasswordPolicies: + if isValidPasswordPart2(pw, pp): inc result diff --git a/2020/src/day3.nim b/2020/src/day3.nim index 08c9fa7..7bf1b1b 100644 --- a/2020/src/day3.nim +++ b/2020/src/day3.nim @@ -3,15 +3,12 @@ import streams proc sled(s: Stream, velx: int, vely: int): int = var xpos, ypos: int for line in s.lines(): - ypos += 1 - if (ypos - 1) mod vely > 0: - continue - if line[xpos mod line.len()] == '#': - result += 1 + inc ypos + if (ypos - 1) mod vely > 0: continue + if line[xpos mod line.len()] == '#': inc result xpos += velx -proc part1*(s: Stream): int = - sled(s, 3, 1) +proc part1*(s: Stream): int = sled(s, 3, 1) proc part2*(s: Stream): int = result = part1(s) diff --git a/2020/src/day_loader.nim b/2020/src/day_loader.nim index 251e8c0..b6c382d 100644 --- a/2020/src/day_loader.nim +++ b/2020/src/day_loader.nim @@ -10,7 +10,7 @@ macro loadDays(): untyped = # TODO: do I "need" to close these streams? solver_str = solver_str & &""" {day}: proc(): tuple[part1: int, part2: int] = - echo "Day {day}" + stdout.write "Day {day}: " ( {module}.part1(getInputFileStreamForDay({day})), {module}.part2(getInputFileStreamForDay({day})) diff --git a/2020/src/input_helpers.nim b/2020/src/input_helpers.nim index 41d6511..d52c97d 100644 --- a/2020/src/input_helpers.nim +++ b/2020/src/input_helpers.nim @@ -1,9 +1,7 @@ import streams, strutils, sugar iterator mapStream*[T](s: Stream, cb: (string) -> T): T = - for line in s.lines(): - yield cb line + for line in s.lines(): yield cb line iterator asInts*(s: Stream): int = - for i in mapStream[int](s, parseInt): - yield i + for i in mapStream[int](s, parseInt): yield i