2021-12-07 11:13:30 -06:00
|
|
|
import std/[streams, sequtils, strutils, sugar, strformat, times, httpclient, os, options]
|
2021-12-02 11:00:05 -06:00
|
|
|
|
2021-12-14 15:18:14 -06:00
|
|
|
type
|
|
|
|
Lines* = seq[string]
|
|
|
|
|
2021-12-02 11:00:05 -06:00
|
|
|
const YEAR = getEnv("AOC_YEAR", "2021").parseInt()
|
|
|
|
proc getCookie(): string = "~/.advent-of-code-auth-cookie".expandTilde().readFile()
|
|
|
|
proc getCacheDir(): string = joinPath(expandTilde("~/.cache"), fmt"/aoc{YEAR}")
|
|
|
|
proc inputFilePath(day: int): string = joinPath(getCacheDir(), fmt"{day}.input")
|
|
|
|
|
|
|
|
proc fetchInput(day: int, filePath: string): StringStream =
|
|
|
|
let client = newHttpClient()
|
|
|
|
client.headers = {"cookie": getCookie()}.newHttpHeaders()
|
|
|
|
let content = client.getContent(fmt"https://adventofcode.com/{YEAR}/day/{day}/input")
|
|
|
|
filePath.writeFile(content)
|
|
|
|
content.newStringStream()
|
|
|
|
|
|
|
|
proc inputStream*(day: int): Stream =
|
|
|
|
getCacheDir().createDir()
|
|
|
|
let cachedFile = day.inputFilePath()
|
|
|
|
if not cachedFile.fileExists(): day.fetchInput(cachedFile)
|
|
|
|
else: openFileStream(cachedFile)
|
|
|
|
|
|
|
|
proc toInts*(s: seq[string]): seq[int] = s.map(parseInt)
|
|
|
|
|
2021-12-06 09:51:20 -06:00
|
|
|
proc loadInputText*(day: int): string = day.inputFilePath().readFile().strip()
|
|
|
|
|
2021-12-14 15:18:14 -06:00
|
|
|
proc loadInput*(day: int): Lines =
|
2021-12-02 11:00:05 -06:00
|
|
|
result = collect:
|
|
|
|
for l in day.inputStream().lines(): l
|
|
|
|
|
|
|
|
template time*(i: string, body: untyped): untyped =
|
|
|
|
let start = cpuTime()
|
|
|
|
body
|
|
|
|
let stop = cpuTime()
|
|
|
|
let diff = $((stop - start) * 1000)
|
2021-12-07 23:05:47 -06:00
|
|
|
echo i & " took " & diff & "ms to process input file and calculate solution"
|
2021-12-02 11:00:05 -06:00
|
|
|
when not defined(release):
|
|
|
|
echo "NOTE: This is not a real measurement of performance. Compile in release mode with -d:release for best performance."
|
2021-12-07 11:13:30 -06:00
|
|
|
|
2021-12-14 15:53:55 -06:00
|
|
|
proc doDay*[T, X](
|
2021-12-07 11:13:30 -06:00
|
|
|
day: int,
|
|
|
|
inputLoader: int -> T,
|
2021-12-14 15:53:55 -06:00
|
|
|
part1: T -> X,
|
|
|
|
part2: T -> X,
|
2021-12-07 11:13:30 -06:00
|
|
|
testInput: T,
|
2021-12-14 15:53:55 -06:00
|
|
|
expectedPart1: X,
|
|
|
|
expectedPart2: X): void =
|
2021-12-07 11:13:30 -06:00
|
|
|
|
2021-12-24 23:25:24 -06:00
|
|
|
when not defined(release):
|
|
|
|
var p1 = testInput.part1()
|
|
|
|
echo "Day ", day, " Part 1: ", p1, " (Expected: ", expectedPart1, ")"
|
|
|
|
doAssert p1 == expectedPart1
|
2021-12-14 15:18:14 -06:00
|
|
|
|
2021-12-24 23:25:24 -06:00
|
|
|
time(&"Day {day} Part 1"): echo day.inputLoader().part1()
|
2021-12-14 15:18:14 -06:00
|
|
|
|
2021-12-24 23:25:24 -06:00
|
|
|
when not defined(release):
|
|
|
|
var p2 = testInput.part2()
|
|
|
|
echo "Day ", day, " Part 2: ", p2, " (Expected: ", expectedPart2, ")"
|
|
|
|
doAssert p2 == expectedPart2
|
2021-12-07 11:46:57 -06:00
|
|
|
|
2021-12-13 14:47:22 -06:00
|
|
|
time(&"Day {day} Part 2"): echo day.inputLoader().part2()
|
|
|
|
|
2021-12-14 15:53:55 -06:00
|
|
|
proc doDayX*[T, X](
|
|
|
|
day: int,
|
|
|
|
inputLoader: int -> T,
|
|
|
|
part1: T -> X,
|
|
|
|
part2: T -> X,
|
|
|
|
testTuple: (T, X, X)): void =
|
|
|
|
let (tin, expectedPart1, expectedPart2) = testTuple
|
|
|
|
doDay(day, inputLoader, part1, part2, tin, expectedPart1, expectedPart2)
|
|
|
|
|
2021-12-13 14:47:22 -06:00
|
|
|
proc doDay*[T](
|
|
|
|
day: int,
|
|
|
|
inputLoader: int -> T,
|
|
|
|
part1: T -> int,
|
|
|
|
part2: T -> int,
|
|
|
|
testTuple: (T, int, int)): void =
|
|
|
|
let (tin, expectedPart1, expectedPart2) = testTuple
|
|
|
|
doDay(day, inputLoader, part1, part2, tin, expectedPart1, expectedPart2)
|
|
|
|
|
2021-12-08 11:33:18 -06:00
|
|
|
proc reduce*[T, X](s: openArray[T], op: (X, T) -> X, init: X): X =
|
2021-12-07 11:46:57 -06:00
|
|
|
result = init
|
|
|
|
for n in s: result = op(result, n)
|
2021-12-08 11:33:18 -06:00
|
|
|
|
|
|
|
proc findFirst*[T](s: openArray[T], op: (T) -> bool): T =
|
|
|
|
for n in s:
|
|
|
|
if op(n): return n
|
2021-12-23 17:16:59 -06:00
|
|
|
|
|
|
|
proc findFirstO*[T](s: openArray[T], op: (T) -> bool): Option[T] =
|
|
|
|
for n in s:
|
|
|
|
if op(n): return some n
|