commit 3cb015b23073aea3b8613e6c46102b9492b437eb Author: Pascal Syma Date: Wed Nov 27 10:10:41 2024 +0100 Initial Commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..81c05ed --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.lockb binary diff=lockb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2930746 --- /dev/null +++ b/.gitignore @@ -0,0 +1,179 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store + + +input.txt +!template/input.txt \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..834d85f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +.output +.nuxt +node_modules +prisma/migrations \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..a72d6ab --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "trailingComma": "none", + "singleQuote": true, + "plugins": ["prettier-plugin-organize-imports"] +} diff --git a/2023/01/2023-01.run.xml b/2023/01/2023-01.run.xml new file mode 100644 index 0000000..2027f8d --- /dev/null +++ b/2023/01/2023-01.run.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/2023/01/index.test.ts b/2023/01/index.test.ts new file mode 100644 index 0000000..1f4aed1 --- /dev/null +++ b/2023/01/index.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'bun:test'; +import { solveFirst, solveSecond } from './index.ts'; + +describe('2023-01', () => { + it('first', () => { + const testInput = `1abc2 +pqr3stu8vwx +a1b2c3d4e5f +treb7uchet`; + expect(solveFirst(testInput)).toBe(142); + }); + it('second', () => { + const testInput = `two1nine +eightwothree +abcone2threexyz +xtwone3four +4nineeightseven2 +zoneight234 +7pqrstsixteen`; + expect(solveSecond(testInput)).toBe(281); + }); +}); diff --git a/2023/01/index.ts b/2023/01/index.ts new file mode 100644 index 0000000..93e8bc3 --- /dev/null +++ b/2023/01/index.ts @@ -0,0 +1,55 @@ +import { sumArray } from '../../utils/array.ts'; + +export function solveFirst(input: string): number { + const regex = /^.*?(\d).*(\d).*?$|^.*(\d).*$/gm; + const match = input.matchAll(regex); + + if (!match) { + return 0; + } + + const numbers = Array.from(match, (m) => + Number(m[3] ? `${m[3]}${m[3]}` : `${m[1]}${m[2]}`) + ); + return numbers.reduce((sum, number) => sum + number, 0); +} + +export function solveSecond(input: string): number { + const lookup: Record = { + one: '1', + two: '2', + three: '3', + four: '4', + five: '5', + six: '6', + seven: '7', + eight: '8', + nine: '9', + '1': '1', + '2': '2', + '3': '3', + '4': '4', + '5': '5', + '6': '6', + '7': '7', + '8': '8', + '9': '9', + '0': '0' + } as const; + + const regex = + /^.*?(one|two|three|four|five|six|seven|eight|nine|\d).*(one|two|three|four|five|six|seven|eight|nine|\d).*?$|^.*(one|two|three|four|five|six|seven|eight|nine|\d).*$/gm; + const match = input.matchAll(regex); + + if (!match) { + return 0; + } + + const numbers = Array.from(match, (m) => + Number( + m[3] ? `${lookup[m[3]]}${lookup[m[3]]}` : `${lookup[m[1]]}${lookup[m[2]]}` + ) + ); + + return sumArray(numbers); +} diff --git a/2023/01/run.ts b/2023/01/run.ts new file mode 100644 index 0000000..88783cd --- /dev/null +++ b/2023/01/run.ts @@ -0,0 +1,12 @@ +import { join } from 'path'; +import { solveFirst, solveSecond } from './index.ts'; + +const input = await Bun.file(join(__dirname, 'input.txt')).text(); + +const firstAnswer = solveFirst(input); + +console.log(firstAnswer); + +const secondAnswer = solveSecond(input); + +console.log(secondAnswer); diff --git a/2023/02/2023-02.run.xml b/2023/02/2023-02.run.xml new file mode 100644 index 0000000..dafb22a --- /dev/null +++ b/2023/02/2023-02.run.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/2023/02/index.test.ts b/2023/02/index.test.ts new file mode 100644 index 0000000..4897621 --- /dev/null +++ b/2023/02/index.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'bun:test'; +import { solveFirst, solveSecond } from './index.ts'; + +describe('2023-02', () => { + const testInput = `Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green +Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue +Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red +Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red +Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green`; + it('first', () => { + expect(solveFirst(testInput, { red: 12, green: 13, blue: 14 })).toBe(8); + }); + it('second', () => { + expect(solveSecond(testInput)).toBe(2286); + }); +}); diff --git a/2023/02/index.ts b/2023/02/index.ts new file mode 100644 index 0000000..2ad4332 --- /dev/null +++ b/2023/02/index.ts @@ -0,0 +1,45 @@ +import { groupBy, multBy, sumArray, sumBy } from '../../utils/array.ts'; + +export type AllowedCubes = Record; + +function parseInput(input: string) { + const matchLine = input.matchAll(/^Game (\d*): (.*)$/gm); + + return Array.from(matchLine, (m) => ({ + id: Number(m[1]), + rounds: m[2].split('; ').map((r) => + r.split(', ').map((g) => { + const [amount, color] = g.split(' '); + return { color, amount: Number(amount) }; + }) + ) + })); +} + +export function solveFirst(input: string, allowed: AllowedCubes): number { + const games = parseInput(input); + + const possible = games.filter( + (g) => + !g.rounds.some((r) => + r.some((s) => !(s.color in allowed) || s.amount > allowed[s.color]) + ) + ); + + return sumBy(possible, (a) => a.id); +} + +export function solveSecond(input: string): number { + const games = parseInput(input); + + const leastCubes = games.map((g) => + groupBy(g.rounds.flat(), (e) => e.color).map(({ key, elements }) => ({ + key, + amount: elements.toSorted((a, b) => b.amount - a.amount)[0].amount + })) + ); + + const powers = leastCubes.map((cubes) => multBy(cubes, (c) => c.amount)); + + return sumArray(powers); +} diff --git a/2023/02/run.ts b/2023/02/run.ts new file mode 100644 index 0000000..66de654 --- /dev/null +++ b/2023/02/run.ts @@ -0,0 +1,12 @@ +import { join } from 'path'; +import { solveFirst, solveSecond } from './index.ts'; + +const input = await Bun.file(join(__dirname, 'input.txt')).text(); + +const firstAnswer = solveFirst(input, { red: 12, green: 13, blue: 14 }); + +console.log(firstAnswer); + +const secondAnswer = solveSecond(input); + +console.log(secondAnswer); diff --git a/README.md b/README.md new file mode 100644 index 0000000..e725b33 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# aoc + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run src/index.ts +``` + +This project was created using `bun init` in bun v1.1.36. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..867e20c Binary files /dev/null and b/bun.lockb differ diff --git a/package.json b/package.json new file mode 100644 index 0000000..10cbb7d --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "aoc", + "type": "module", + "author": "Pascal Syma (https://syma.dev/)", + "scripts": { + "format": "prettier --write .", + "check": "prettier --check .", + "test": "bun test", + "test:dev": "bun test --watch" + }, + "devDependencies": { + "@types/bun": "latest", + "prettier": "^3.4.0", + "prettier-plugin-organize-imports": "^4.1.0" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } +} diff --git a/template/20xx-xx.run.xml b/template/20xx-xx.run.xml new file mode 100644 index 0000000..ceca224 --- /dev/null +++ b/template/20xx-xx.run.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/template/index.test.ts b/template/index.test.ts new file mode 100644 index 0000000..0ed9234 --- /dev/null +++ b/template/index.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'bun:test'; +import { solveFirst, solveSecond } from './index.ts'; + +describe('20xx-xx', () => { + it('first', () => { + // TODO: add first input + const testInput = ``; + // TODO: add first solution + expect(solveFirst(testInput)).toBe(0); + }); + it('second', () => { + // TODO: add second input + const testInput = ``; + // TODO: add second solution + expect(solveSecond(testInput)).toBe(0); + }); +}); diff --git a/template/index.ts b/template/index.ts new file mode 100644 index 0000000..b1ce7ff --- /dev/null +++ b/template/index.ts @@ -0,0 +1,11 @@ +export function solveFirst(input: string): number { + // TODO: add code + + return 0; +} + +export function solveSecond(input: string): number { + // TODO: add code + + return 0; +} diff --git a/template/input.txt b/template/input.txt new file mode 100644 index 0000000..e69de29 diff --git a/template/run.ts b/template/run.ts new file mode 100644 index 0000000..88783cd --- /dev/null +++ b/template/run.ts @@ -0,0 +1,12 @@ +import { join } from 'path'; +import { solveFirst, solveSecond } from './index.ts'; + +const input = await Bun.file(join(__dirname, 'input.txt')).text(); + +const firstAnswer = solveFirst(input); + +console.log(firstAnswer); + +const secondAnswer = solveSecond(input); + +console.log(secondAnswer); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..238655f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/utils/array.ts b/utils/array.ts new file mode 100644 index 0000000..6f8928f --- /dev/null +++ b/utils/array.ts @@ -0,0 +1,46 @@ +export function sumArray(arr: Array): number { + return sumBy(arr, (a) => a); +} + +export function sumBy( + arr: Array, + by: (array: T) => number | string +): number { + return arr.reduce((sum, element) => sum + Number(by(element)), 0); +} + +export function multArray(arr: Array): number { + return multBy(arr, (a) => a); +} + +export function multBy( + arr: Array, + by: (array: T) => number | string +): number { + if (arr.length <= 0) { + return 0; + } + return arr.reduce((sum, element) => sum * Number(by(element)), 1); +} + +export function groupBy( + arr: Array, + by: (element: E) => K +): { key: K; elements: Array }[] { + return arr.reduce( + (groups, element) => { + const key = by(element); + const index = groups.findIndex((e) => e.key === key); + + if (index < 0) { + groups.push({ key, elements: [element] }); + return groups; + } + const existing = groups[index]; + existing.elements.push(element); + + return groups; + }, + [] as { key: K; elements: Array }[] + ); +}