diff --git a/2021/1.ts b/2021/1.ts index 277ddfd..b10ba7a 100644 --- a/2021/1.ts +++ b/2021/1.ts @@ -1,53 +1,52 @@ -import { readLines } from "./deps.ts"; +import { inputNumbers } from "./common.ts"; -// Day 1 -/* - * This solution is relatively simple. Keep track of the last depth we measured - * in `lastDepth` (skipping the first line of input by defaulting it to - * `Number.MAX_VALUE`) and compare it to each line of input as we read it. Once - * we've compared, set the `lastDepth` to the current depth for the next +/** This solution is relatively simple. Keep track of the last depth we + * measured in `lastDepth` (skipping the first line of input by defaulting it + * to `Number.MAX_VALUE`) and compare it to each line of input as we read it. + * Once we've compared, set the `lastDepth` to the current depth for the next * iteration. * * This solution should be O(n) for compute and O(n) for memory since we * iterate the input once and only read a line at a time, we actually have O(1) * memory, but since the input is on disk, that's kind of unfair to say. */ -let part1Increases = 0; -let lastDepth = Number.MAX_VALUE; -for await (const depthString of readLines(await Deno.open("1.input"))) { - const depth = parseInt(depthString); - if (depth > lastDepth) part1Increases++; - lastDepth = depth; +export async function part1( + input: AsyncIterableIterator, +): Promise { + let increases = 0; + let lastDepth = Number.MAX_VALUE; + for await (const depth of input) { + if (depth > lastDepth) increases++; + lastDepth = depth; + } + return increases; } -console.log("Number of depth increases (Part 1):", part1Increases); +console.log("Part 1", await part1(await inputNumbers("1"))); -// Day 2 -/* - * Since the windows we need to compare overlap, we really only need to compare - * the values unique to each window, which will be the first of the first window - * and the last of the last window. Since the windows have size 3, we really - * only need to compare a value with the "three-lines-ago" value. This solution - * otherwise is identical to the previous one. +/** Since the windows we need to compare overlap, we really only need to + * compare the values unique to each window, which will be the first of the + * first window and the last of the last window. Since the windows have size 3, + * we really only need to compare a value with the "three-lines-ago" value. + * This solution otherwise is identical to the previous one. */ -let part2Increases = 0; -const WINDOW_SIZE = 3; -const input = readLines(await Deno.open("1.input")); +export async function part2( + input: AsyncIterableIterator, + windowSize = 3, +): Promise { + let increases = 0; + // TODO: use a proper FIFO queue data structure for this? + let window: number[] = await Promise.all(Array.from( + Array(windowSize), + async (_x, _i) => parseInt((await input.next()).value), + )); -// TODO: use a proper FIFO queue data structure for this? -// read the first `WINDOW_SIZE` values from our input in preparation -let window: number[] = await Promise.all(Array.from( - Array(WINDOW_SIZE), - async (_x, _i) => parseInt((await input.next()).value), -)); - -for await (const depthString of input) { - const depth = parseInt(depthString); - // compare the current depth to the depth we read `WINDOW_SIZE` ago - if (depth > window[0]) part2Increases++; - // now we push the current depth onto the window and cut the one we just compared off - // TODO: if this was a proper queue, we could just push it on the queue and - // expect the first-in value to fall out - window = window.slice(1); - window.push(depth); + for await (const depth of input) { + if (depth > window[0]) increases++; + // TODO: if this was a proper queue, we could just push it on the queue and + // expect the first-in value to fall out + window = window.slice(1); + window.push(depth); + } + return increases; } -console.log("Number of summed depth increases (Part 2):", part2Increases); +console.log("Part 2", await part2(await inputNumbers("1"))); diff --git a/2021/common.ts b/2021/common.ts new file mode 100644 index 0000000..c663726 --- /dev/null +++ b/2021/common.ts @@ -0,0 +1,72 @@ +import { ensureDir, path, readAll, readLines } from "./deps.ts"; + +export const HOME = Deno.env.get("HOME") || "/tmp"; +export const XDG_CACHE_HOME = Deno.env.get("XDG_CACHE_HOME") || + path.join(HOME, ".config"); + +const INPUT_CACHE_DIR = path.join(XDG_CACHE_HOME, "aoc2021"); + +async function fileExists(filePath: string) { + try { + await Deno.lstat(filePath); + return true; + } catch (e) { + if (e instanceof Deno.errors.NotFound) { + return false; + } + throw e; + } +} + +function inputFilePath(day: string) { + return path.join(INPUT_CACHE_DIR, `${day}.input`); +} + +async function ensureInput(day: string) { + const filePath = inputFilePath(day); + await ensureDir(INPUT_CACHE_DIR); + if (await fileExists(filePath)) return; + const cookieFile = path.join(HOME, ".advent-of-code-2021-auth-cookie"); + let cookie = ""; + try { + cookie = Deno.env.get("ADVENT_OF_CODE_2021_AUTH_COOKIE") || + new TextDecoder().decode( + await readAll(await Deno.open(cookieFile)), + ); + } catch (e) { + console.error( + `Failed to load adventofcode.com cookie from ADVENT_OF_CODE_2021_AUTH_COOKIE environment variable or from file ${cookieFile}`, + ); + throw e; + } + const url = `https://adventofcode.com/2021/day/${day}/input`; + const response = await fetch(url, { headers: { cookie } }); + await Deno.writeTextFile(filePath, await response.text()); +} + +async function fileLines( + filePath: string, +): Promise> { + return readLines(await Deno.open(filePath)); +} + +export async function inputLines( + day: string, +): Promise> { + await ensureInput(day); + return fileLines(inputFilePath(day)); +} + +async function* readStringsAsNumbers( + input: AsyncIterableIterator, +): AsyncIterableIterator { + for await (const s of input) { + yield parseInt(s); + } +} + +export async function inputNumbers( + day: string, +): Promise> { + return readStringsAsNumbers(await inputLines(day)); +} diff --git a/2021/deps.ts b/2021/deps.ts index 64ea606..655af59 100644 --- a/2021/deps.ts +++ b/2021/deps.ts @@ -1 +1,4 @@ export { readLines } from "https://deno.land/std@0.116.0/io/mod.ts"; +export { ensureDir } from "https://deno.land/std@0.110.0/fs/mod.ts"; +export * as path from "https://deno.land/std@0.116.0/path/mod.ts"; +export { readAll } from "https://deno.land/std@0.116.0/streams/conversion.ts";