From ea0a467b5c8052b16661367c0843d351d8f4b567 Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Thu, 2 Dec 2021 11:00:05 -0600 Subject: [PATCH] Lots of cleanup for nim solutions and day 2 solutions in nim --- 2021/.gitignore | 1 + 2021/2.ts | 12 +++++++---- 2021/common.nim | 35 ++++++++++++++++++++++++++++++++ 2021/one.nim | 38 ++++++++++------------------------- 2021/readme.md | 20 ++++++++++++++++++- 2021/two.nim | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 32 deletions(-) create mode 100644 2021/common.nim create mode 100644 2021/two.nim diff --git a/2021/.gitignore b/2021/.gitignore index 63ff173..90d20fe 100644 --- a/2021/.gitignore +++ b/2021/.gitignore @@ -1 +1,2 @@ *.input +build diff --git a/2021/2.ts b/2021/2.ts index e3cb375..2699d94 100644 --- a/2021/2.ts +++ b/2021/2.ts @@ -1,11 +1,18 @@ import { collectArray, inputLines, measureDuration } from "./common.ts"; const input = await collectArray(await inputLines("2")); +type SubmarineCommand = ["forward", number] | ["up", number] | ["down", number]; + +// function parseSubmarineCommand(command: string): SubmarineCommand { +// let [cmd, arg] = command.split(" ", 2); +// cmd = ["forward", "up", "down"].includes(cmd) ? cmd : "up"; +// return [cmd, parseInt(arg)]; +// } + export function part1(input: string[]): number { let x = 0; let y = 0; for (const line of input) { - console.log(line); if (line.startsWith("forward ")) { x += parseInt(line.substr(8)); } else if (line.startsWith("up ")) { @@ -24,7 +31,6 @@ export function part2(input: string[]): number { let y = 0; let aim = 0; for (const line of input) { - console.log(line); if (line.startsWith("forward ")) { const arg = parseInt(line.substr(8)); x += arg; @@ -32,11 +38,9 @@ export function part2(input: string[]): number { } else if (line.startsWith("up ")) { const arg = parseInt(line.substr(3)); aim -= arg; - // y -= arg; } else if (line.startsWith("down ")) { const arg = parseInt(line.substr(5)); aim += arg; - // y += arg; } } return x * y; diff --git a/2021/common.nim b/2021/common.nim new file mode 100644 index 0000000..f39e23a --- /dev/null +++ b/2021/common.nim @@ -0,0 +1,35 @@ +import std/[streams, sequtils, strutils, sugar, strformat, times, httpclient, os] + +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) + +proc loadInput*(day: int): seq[string] = + 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) + echo i & " took " & diff & "ms to calculate solution" + when not defined(release): + echo "NOTE: This is not a real measurement of performance. Compile in release mode with -d:release for best performance." + # jkp: {(stop - start) * 1000} ms to calculate solution: {result}" diff --git a/2021/one.nim b/2021/one.nim index 321c9ca..d82115d 100644 --- a/2021/one.nim +++ b/2021/one.nim @@ -1,31 +1,15 @@ -import std/[streams, strutils, sugar, strformat, times] +import ./common -iterator inputForDay(day: int): int = - var stream: FileStream = fmt"/home/daniel/.home/.config/aoc2021/{day}.input".openFileStream - for s in stream.lines(): - yield s.parseInt - -proc part1(inputs: seq[int], dist=1): int = - result = 0 - var i = dist - var x = inputs.len()-1 - while i <= x: +proc countDepthIncreases(inputs: seq[int], dist=1): int = + for i in dist.. inputs[i-dist]: inc result - inc i -let dd1 = epochTime() -var input = collect(newSeq): - for i in inputForDay(1): i +let input = 1.loadInput().toInts() +time("countDepthIncreases part 1"): echo input.countDepthIncreases() +time("countDepthIncreases part 2"): echo input.countDepthIncreases(3) -let dd2 = epochTime() -echo &"{(dd2 - dd1) * 1000} ms (to load input)" -echo "Part 1" -let d1 = epochTime() -echo part1(input) -let d2 = epochTime() -echo &"{(d2 - d1) * 1000} ms (to calculate solution)" -echo "Part 2" -let d21 = epochTime() -echo part1(input,3) -let d22 = epochTime() -echo &"{(d22 - d21) * 1000} ms (to calculate solution)" +when not defined(release): + static: + let testInputs = @[199, 200, 208, 210, 200, 207, 240, 269, 260, 263] + doAssert testInputs.countDepthIncreases() == 7 + doAssert testInputs.countDepthIncreases(3) == 5 diff --git a/2021/readme.md b/2021/readme.md index 600d513..571f558 100644 --- a/2021/readme.md +++ b/2021/readme.md @@ -12,16 +12,34 @@ Specifically, here's my `deno --version` output: Enjoy! +**EDIT**: Since performance is not what I would like, it looks like I'm also doing some of these in nim. + ## Usage Run these solutions like so: deno run --unstable --allow-all $DAY.ts +And the nim ones like so: + + nim c -d:release -d:ssl --run $DAYMODULE.nim + +And if you want to measure memory usage: + + mkdir -p build + deno compile --output build/$DAY --unstable --allow-all $DAY.ts + /usr/bin/time -v ./build/$DAY + +Or + + mkdir -p build + nim c -d:release -d:ssl --outdir:build $DAYMODULE.nim + /usr/bin/time -v ./$DAYMODULE + # Days - [x] [Day 1](./1.ts) -- [ ] Day 2 +- [x] [Day 2](./2.ts) - [ ] Day 3 - [ ] Day 4 - [ ] Day 5 diff --git a/2021/two.nim b/2021/two.nim new file mode 100644 index 0000000..6337de2 --- /dev/null +++ b/2021/two.nim @@ -0,0 +1,53 @@ +import std/[strutils, sequtils] +import ./common + +type SubmarineCommand = enum + Forward, Up, Down + +proc parseArg(c: char): int = c.int() - 48 + +proc parseSubmarineCommand(l: string): (SubmarineCommand, int) = + case l[0]: + # since each arg is only one char, we can use this much faster method to + # convert to an integer + of 'd': return (Down, l[5].parseArg()) + of 'u': return (Up, l[3].parseArg()) + else: return (Forward, l[8].parseArg()) + +proc submarineCommands(inputs: seq[string]): int = + var x, y = 0 + for (cmd, arg) in inputs.map(parseSubmarineCommand): + case cmd: + of Up: y -= arg + of Down: y += arg + of Forward: x += arg + x * y + +proc submarineCommandsWithAim(inputs: seq[string]): int = + var x, y, aim = 0 + for (cmd, arg) in inputs.map(parseSubmarineCommand): + case cmd: + of Up: aim -= arg + of Down: aim += arg + of Forward: + x += arg + y += aim * arg + x * y + +let input = 2.loadInput() +time("submarineCommands part 1"): echo input.submarineCommands() +time("submarineCommandsWithAim part 2"): echo input.submarineCommandsWithAim() + +when not defined(release): + static: + let testInput = @[ + "forward 5", + "down 5", + "forward 8", + "up 3", + "down 8", + "forward 2", + ] + doAssert testInput.submarineCommands() == 150 + doAssert testInput.submarineCommandsWithAim() == 900 +