diff --git a/2022/01/main.js b/2022/01/main.js new file mode 100644 index 0000000..453167a --- /dev/null +++ b/2022/01/main.js @@ -0,0 +1,11 @@ +import { expect } from "vitest"; + +export const spy = (x) => { + console.debug(x); + return x; +}; + +export function expectSolution(result) { + console.info(result); + return expect(result); +} diff --git a/2022/01/test.js b/2022/01/test.js index a573612..7da2d97 100644 --- a/2022/01/test.js +++ b/2022/01/test.js @@ -1,16 +1,14 @@ import * as R from "remeda"; import { test, expect } from "vitest"; import fs from "fs-extra"; +import path from 'path'; +import { expectSolution } from './main.js'; const split = (splitter) => (text) => text.split(splitter); const sum = R.sumBy(R.identity); -const spy = (x) => { - console.debug(x); - return x; -}; const input = R.pipe( - fs.readFileSync("input", "utf8"), + fs.readFileSync( path.join( path.dirname(import.meta.url), "input").replace('file:',''), "utf8"), split("\n\n"), R.map((x) => split("\n")(x) @@ -20,10 +18,6 @@ const input = R.pipe( R.map(sum) ); -function expectSolution(result) { - console.info(result); - return expect(result); -} test("part 1", () => { const maxCalories = R.pipe(input, (calories) => Math.max(...calories)); diff --git a/2022/02/part1.js b/2022/02/part1.js new file mode 100644 index 0000000..2d3eb9c --- /dev/null +++ b/2022/02/part1.js @@ -0,0 +1,60 @@ +import * as R from "remeda"; +import fs from "fs-extra"; +import path from "path"; + +export const sampleInput = `A Y +B X +C Z`; + +export const puzzleInput = fs.readFileSync( + path.join(path.dirname(import.meta.url), "input").replace("file:", ""), + "utf8" +); + +const playMap = { + X: "A", // rock + Y: "B", // paper + Z: "C", // scissors +}; + +export const parseInput = R.createPipe( + (text) => text.split("\n"), + R.filter(R.identity), // remove last line + R.map((entry) => entry.split(" ")) +); + +export const movePositions = ["A", "B", "C"]; + +const playScore = { + A: 1, + B: 2, + C: 3, +}; + +const draw = 3; +const lost = 0; +const win = 6; + +const outcome = (opponent, me) => { + if (opponent === me) return draw; + if (opponent === "A") { + return me === "C" ? lost : win; + } + if (opponent === "B") { + return me === "A" ? lost : win; + } + return me === "B" ? lost : win; +}; + +export const mapScores = ([opponent, me]) => [ + outcome(opponent, me), + playScore[me], +]; + +export const solutionPart1 = R.createPipe( + parseInput, + R.map(([a, b]) => [a, playMap[b]]), + R.map(mapScores), + R.map(R.sumBy(R.identity)), + R.sumBy(R.identity) +); diff --git a/2022/02/part2.js b/2022/02/part2.js new file mode 100644 index 0000000..c70ebe1 --- /dev/null +++ b/2022/02/part2.js @@ -0,0 +1,25 @@ +import * as R from "remeda"; + +import { movePositions, mapScores, playMap, parseInput } from "./part1.js"; + +function calculateMove(opponent, verdict) { + const incr = { + Y: 0, + X: 2, + Z: 1, + }; + + const index = + (movePositions.findIndex((x) => x === opponent) + incr[verdict]) % + movePositions.length; + + return movePositions[index]; +} + +export const solutionPart2 = R.createPipe( + parseInput, + R.map(([a, b]) => [a, calculateMove(a, b)]), + R.map(mapScores), + R.map(R.sumBy(R.identity)), + R.sumBy(R.identity) +); diff --git a/2022/02/test.js b/2022/02/test.js new file mode 100644 index 0000000..778928a --- /dev/null +++ b/2022/02/test.js @@ -0,0 +1,41 @@ +import { test, expect, describe } from "vitest"; + +import { expectSolution } from "../01/main.js"; +import { + parseInput, + sampleInput, + puzzleInput, + mapScores, + solutionPart1, +} from "./part1.js"; +import { solutionPart2 } from "./part2.js"; + +describe("part 1", () => { + test("input parsing", () => { + expect(parseInput(sampleInput)).toEqual([ + ["A", "Y"], + ["B", "X"], + ["C", "Z"], + ]); + }); + + test("mapScore", () => { + expect(mapScores(["A", "B"])).toEqual([6, 2]); + expect(mapScores(["B", "A"])).toEqual([0, 1]); + expect(mapScores(["C", "C"])).toEqual([3, 3]); + }); + + test("part 1, sample", () => { + expect(solutionPart1(sampleInput)).toEqual(15); + }); + + test("solution", () => { + expectSolution(solutionPart1(puzzleInput)).toEqual(12458); + }); +}); + +describe("part 2", () => { + test("part 2", () => { + expectSolution(solutionPart2(puzzleInput)).toEqual(12683); + }); +}); diff --git a/2022/03/part1.js b/2022/03/part1.js new file mode 100644 index 0000000..7366b96 --- /dev/null +++ b/2022/03/part1.js @@ -0,0 +1,20 @@ +import * as R from "remeda"; +import fs from "fs-extra"; +import path from "path"; + +export const readFile = (year, day, file) => + fs.readFileSync(path.join(year, day, file), "utf8"); + +export const sample = readFile("2022", "03", "sample"); +export const puzzleInput = readFile("2022", "03", "input"); + +export const solutionPart1 = R.createPipe( + (text) => text.split("\n"), + R.filter(R.identity), + R.map((line) => line.split("")), + R.map((line) => [line, line.splice(line.length / 2)]), + R.map((line) => R.intersection(...line)), + R.map((line) => line[0].charCodeAt(0)), + R.map((code) => (code > 96 ? code - 96 : code - 38)), + R.sumBy(R.identity) +); diff --git a/2022/03/part2.js b/2022/03/part2.js new file mode 100644 index 0000000..b40e97e --- /dev/null +++ b/2022/03/part2.js @@ -0,0 +1,12 @@ +import * as R from "remeda"; + +export const solutionPart2 = R.createPipe( + (text) => text.split("\n"), + R.filter(R.identity), + R.map((line) => line.split("")), + R.chunk(3), + R.map((group) => group.reduce((a, b) => R.intersection(a, b))), + R.map((line) => line[0].charCodeAt(0)), + R.map((code) => (code > 96 ? code - 96 : code - 38)), + R.sumBy(R.identity) +); diff --git a/2022/03/sample b/2022/03/sample new file mode 100644 index 0000000..f17e726 --- /dev/null +++ b/2022/03/sample @@ -0,0 +1,6 @@ +vJrwpWtwJgWrhcsFMMfFFhFp +jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL +PmmdzqPrVvPwwTWBwg +wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn +ttgJtRGJQctTZtZT +CrZsJsPPZsGzwwsLwLmpwMDw diff --git a/2022/03/test.js b/2022/03/test.js new file mode 100644 index 0000000..4f1e0f8 --- /dev/null +++ b/2022/03/test.js @@ -0,0 +1,25 @@ +import { test, expect, describe } from "vitest"; + +import { solutionPart1, sample, puzzleInput } from "./part1.js"; + +import { solutionPart2 } from "./part2.js"; + +function expectSolution(result) { + console.info(result); + return expect(result); +} + +describe("part 1", () => { + test("sample", () => { + expect(solutionPart1(sample)).toEqual(157); + }); + test("solution", () => { + expectSolution(solutionPart1(puzzleInput)).toEqual(8515); + }); +}); + +describe("part 2", () => { + test("solution", () => { + expectSolution(solutionPart2(puzzleInput)).toEqual(2434); + }); +}); diff --git a/2022/04/part1.js b/2022/04/part1.js new file mode 100644 index 0000000..7a346cb --- /dev/null +++ b/2022/04/part1.js @@ -0,0 +1,20 @@ +import * as R from "remeda"; + +import { readFile } from "../03/part1.js"; + +export const sample = readFile("2022", "04", "sample"); +export const puzzleInput = readFile("2022", "04", "input"); + +const rangeLength = ([a, b]) => b - a + 1; + +const isContainedBy = ([a1, a2], [b1, b2]) => a1 >= b1 && a2 <= b2; + +export const solutionPart1 = R.createPipe( + (text) => text.split("\n"), + R.filter(R.identity), + R.map((line) => + line.split(",").map((range) => range.split("-").map((x) => parseInt(x))) + ), + R.map(R.sortBy((x) => rangeLength(x))), + R.countBy(([a, b]) => isContainedBy(a, b)) +); diff --git a/2022/04/part2.js b/2022/04/part2.js new file mode 100644 index 0000000..744c09b --- /dev/null +++ b/2022/04/part2.js @@ -0,0 +1,13 @@ +import * as R from "remeda"; + +const overlapsWith = ([a1, a2], [b1, b2]) => a2 >= b1; + +export const solutionPart2 = R.createPipe( + (text) => text.split("\n"), + R.filter(R.identity), + R.map((line) => + line.split(",").map((range) => range.split("-").map((x) => parseInt(x))) + ), + R.map(R.sortBy(([x]) => x)), + R.countBy(([a, b]) => overlapsWith(a, b)) +); diff --git a/2022/04/sample b/2022/04/sample new file mode 100644 index 0000000..9f9e9cf --- /dev/null +++ b/2022/04/sample @@ -0,0 +1,6 @@ +2-4,6-8 +2-3,4-5 +5-7,7-9 +2-8,3-7 +6-6,4-6 +2-6,4-8 diff --git a/2022/04/test.js b/2022/04/test.js new file mode 100644 index 0000000..a96bd6e --- /dev/null +++ b/2022/04/test.js @@ -0,0 +1,23 @@ +import { test, describe } from "vitest"; + +import { expectSolution } from "../01/main.js"; +import { solutionPart1, puzzleInput, sample } from "./part1.js"; +import { solutionPart2 } from "./part2.js"; + +describe("part 1", () => { + test("sample", () => { + expectSolution(solutionPart1(sample)).toEqual(2); + }); + test("solution", () => { + expectSolution(solutionPart1(puzzleInput)).toEqual(605); + }); +}); + +describe("part 2", () => { + test("sample", () => { + expectSolution(solutionPart2(sample)).toEqual(4); + }); + test("solution", () => { + expectSolution(solutionPart2(puzzleInput)).toEqual(914); + }); +}); diff --git a/2022/template/part1.js b/2022/template/part1.js new file mode 100644 index 0000000..b509eb0 --- /dev/null +++ b/2022/template/part1.js @@ -0,0 +1 @@ +import * as R from "remeda"; diff --git a/2022/template/part2.js b/2022/template/part2.js new file mode 100644 index 0000000..31477e8 --- /dev/null +++ b/2022/template/part2.js @@ -0,0 +1,2 @@ +import * as R from "remeda"; + diff --git a/2022/template/test.js b/2022/template/test.js new file mode 100644 index 0000000..9859a0e --- /dev/null +++ b/2022/template/test.js @@ -0,0 +1,20 @@ +import { test, expect, describe } from "vitest"; + +import { expectSolution } from "../01/main.js"; +import { + solutionPart1, + puzzleInput, +} from "./part1.js"; +import { solutionPart2 } from "./part2.js"; + +describe("part 1", () => { + test.todo("solution", () => { + expectSolution(solutionPart1(puzzleInput)).toEqual('TODO'); + }); +}); + +describe("part 2", () => { + test.todo("solution", () => { + expectSolution(solutionPart2(puzzleInput)).toEqual('TODO'); + }); +});