Add basic input fetching logic and common helpers
This commit is contained in:
parent
198ac267de
commit
c1fbaf44e4
81
2021/1.ts
81
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
|
||||||
* This solution is relatively simple. Keep track of the last depth we measured
|
* to `Number.MAX_VALUE`) and compare it to each line of input as we read it.
|
||||||
* in `lastDepth` (skipping the first line of input by defaulting it to
|
* Once we've compared, set the `lastDepth` to the current depth for the next
|
||||||
* `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.
|
* iteration.
|
||||||
*
|
*
|
||||||
* This solution should be O(n) for compute and O(n) for memory since we
|
* 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)
|
* 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.
|
* memory, but since the input is on disk, that's kind of unfair to say.
|
||||||
*/
|
*/
|
||||||
let part1Increases = 0;
|
export async function part1(
|
||||||
let lastDepth = Number.MAX_VALUE;
|
input: AsyncIterableIterator<number>,
|
||||||
for await (const depthString of readLines(await Deno.open("1.input"))) {
|
): Promise<number> {
|
||||||
const depth = parseInt(depthString);
|
let increases = 0;
|
||||||
if (depth > lastDepth) part1Increases++;
|
let lastDepth = Number.MAX_VALUE;
|
||||||
lastDepth = depth;
|
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
|
||||||
* Since the windows we need to compare overlap, we really only need to compare
|
* first window and the last of the last window. Since the windows have size 3,
|
||||||
* the values unique to each window, which will be the first of the first window
|
* we really only need to compare a value with the "three-lines-ago" value.
|
||||||
* and the last of the last window. Since the windows have size 3, we really
|
* This solution otherwise is identical to the previous one.
|
||||||
* only need to compare a value with the "three-lines-ago" value. This solution
|
|
||||||
* otherwise is identical to the previous one.
|
|
||||||
*/
|
*/
|
||||||
let part2Increases = 0;
|
export async function part2(
|
||||||
const WINDOW_SIZE = 3;
|
input: AsyncIterableIterator<number>,
|
||||||
const input = readLines(await Deno.open("1.input"));
|
windowSize = 3,
|
||||||
|
): Promise<number> {
|
||||||
|
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?
|
for await (const depth of input) {
|
||||||
// read the first `WINDOW_SIZE` values from our input in preparation
|
if (depth > window[0]) increases++;
|
||||||
let window: number[] = await Promise.all(Array.from(
|
// TODO: if this was a proper queue, we could just push it on the queue and
|
||||||
Array(WINDOW_SIZE),
|
// expect the first-in value to fall out
|
||||||
async (_x, _i) => parseInt((await input.next()).value),
|
window = window.slice(1);
|
||||||
));
|
window.push(depth);
|
||||||
|
}
|
||||||
for await (const depthString of input) {
|
return increases;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
console.log("Number of summed depth increases (Part 2):", part2Increases);
|
console.log("Part 2", await part2(await inputNumbers("1")));
|
||||||
|
|
72
2021/common.ts
Normal file
72
2021/common.ts
Normal file
|
@ -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<AsyncIterableIterator<string>> {
|
||||||
|
return readLines(await Deno.open(filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function inputLines(
|
||||||
|
day: string,
|
||||||
|
): Promise<AsyncIterableIterator<string>> {
|
||||||
|
await ensureInput(day);
|
||||||
|
return fileLines(inputFilePath(day));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* readStringsAsNumbers(
|
||||||
|
input: AsyncIterableIterator<string>,
|
||||||
|
): AsyncIterableIterator<number> {
|
||||||
|
for await (const s of input) {
|
||||||
|
yield parseInt(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function inputNumbers(
|
||||||
|
day: string,
|
||||||
|
): Promise<AsyncIterableIterator<number>> {
|
||||||
|
return readStringsAsNumbers(await inputLines(day));
|
||||||
|
}
|
|
@ -1 +1,4 @@
|
||||||
export { readLines } from "https://deno.land/std@0.116.0/io/mod.ts";
|
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";
|
||||||
|
|
Loading…
Reference in a new issue