From 2ae2c2ba0fe4aa14764854649c3eae64585ed154 Mon Sep 17 00:00:00 2001 From: wieerwill Date: Sun, 10 Dec 2023 12:32:32 +0100 Subject: [PATCH] solve Day08 in JS/TS --- Day08/js/solution.js | 82 ++++++++++++++++++++++++++++++++++++++++++++ Day08/ts/solution.ts | 82 ++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 14 ++++++++ package.json | 1 + tsconfig.json | 2 +- 5 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 Day08/js/solution.js create mode 100644 Day08/ts/solution.ts diff --git a/Day08/js/solution.js b/Day08/js/solution.js new file mode 100644 index 0000000..eeffe6c --- /dev/null +++ b/Day08/js/solution.js @@ -0,0 +1,82 @@ +const fs = require('fs'); +const path = require('path'); + +function gcd(a, b) { + while (b !== 0) { + let t = b; + b = a % b; + a = t; + } + return a; +} + +function lcm(a, b) { + return (a * b) / gcd(a, b); +} + +function parseFile(filePath) { + console.log(`Parsing file: ${filePath}`); + const data = fs.readFileSync(filePath, 'utf8').trim(); + const [stepsSection, rulesSection] = data.split('\n\n'); + const steps = stepsSection.split('').map(char => char === 'L' ? 0 : 1); + const rules = rulesSection.split('\n').reduce((acc, line) => { + const [state, directions] = line.split('=').map(s => s.trim()); + const [left, right] = directions.split(',').map(s => s.trim()); + acc['L'][state] = left.slice(1); + acc['R'][state] = right.slice(0, -1); + return acc; + }, { 'L': {}, 'R': {} }); + + return { steps, rules }; +} + +function navigateNetworkSimultaneously(steps, rules) { + console.log("Starting navigation of network."); + const startNodes = Object.keys(rules['L']).filter(node => node.endsWith('A')); + let currentNodes = startNodes; + let stepCount = 0; + const timeToZ = {}; + + while (Object.keys(timeToZ).length < startNodes.length) { + currentNodes.forEach((node, index) => { + const direction = steps[stepCount % steps.length] === 0 ? 'L' : 'R'; + const nextNode = rules[direction][node]; + currentNodes[index] = nextNode; + if (nextNode.endsWith('Z') && !(index in timeToZ)) { + timeToZ[index] = stepCount + 1; + } + }); + stepCount++; + console.log(`Step ${stepCount}: Current nodes - ${currentNodes}`); + } + + return Object.values(timeToZ).reduce((acc, val) => lcm(acc, val), 1); +} + +function runTest() { + console.log("Running test..."); + const testPath = path.join(__dirname, '../test.txt'); + const { steps, rules } = parseFile(testPath); + const expected = 6; + const result = navigateNetworkSimultaneously(steps, rules); + + if (result !== expected) { + throw new Error(`Test failed: expected ${expected}, got ${result}`); + } + + console.log(`Test passed with ${result} steps.`); +} + +function main() { + try { + runTest(); + const inputPath = path.join(__dirname, '../input.txt'); + const { steps, rules } = parseFile(inputPath); + const result = navigateNetworkSimultaneously(steps, rules); + console.log(`All paths reached 'Z' nodes simultaneously in ${result} steps.`); + } catch (error) { + console.error(`Error: ${error.message}`); + } +} + +main(); diff --git a/Day08/ts/solution.ts b/Day08/ts/solution.ts new file mode 100644 index 0000000..ec93040 --- /dev/null +++ b/Day08/ts/solution.ts @@ -0,0 +1,82 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +function gcd(a: number, b: number): number { + while (b !== 0) { + let t = b; + b = a % b; + a = t; + } + return a; +} + +function lcm(a: number, b: number): number { + return (a * b) / gcd(a, b); +} + +function parseFile(filePath: string): { steps: number[], rules: { [key: string]: { [key: string]: string } } } { + console.log(`Parsing file: ${filePath}`); + const data = fs.readFileSync(filePath, 'utf8').trim(); + const [stepsSection, rulesSection] = data.split('\n\n'); + const steps = stepsSection.split('').map((char: string) => char === 'L' ? 0 : 1); + const rules = rulesSection.split('\n').reduce((acc: { [key: string]: { [key: string]: string } }, line: string) => { + const [state, directions] = line.split('=').map((s: string) => s.trim()); + const [left, right] = directions.split(',').map((s: string) => s.trim()); + acc['L'][state] = left.slice(1); + acc['R'][state] = right.slice(0, -1); + return acc; + }, { 'L': {}, 'R': {} }); + + return { steps, rules }; +} + +function navigateNetworkSimultaneously(steps: number[], rules: { [key: string]: { [key: string]: string } }): number { + console.log("Starting navigation of network."); + const startNodes = Object.keys(rules['L']).filter((node: string) => node.endsWith('A')); + let currentNodes = startNodes; + let stepCount = 0; + const timeToZ: { [key: number]: number } = {}; + + while (Object.keys(timeToZ).length < startNodes.length) { + currentNodes.forEach((node: string, index: number) => { + const direction = steps[stepCount % steps.length] === 0 ? 'L' : 'R'; + const nextNode = rules[direction][node]; + currentNodes[index] = nextNode; + if (nextNode.endsWith('Z') && !(index in timeToZ)) { + timeToZ[index] = stepCount + 1; + } + }); + stepCount++; + console.log(`Step ${stepCount}: Current nodes - ${currentNodes}`); + } + + return Object.values(timeToZ).reduce((acc: number, val: number) => lcm(acc, val), 1); +} + +function runTest(): void { + console.log("Running test..."); + const testPath = path.join(__dirname, '../test.txt'); + const { steps, rules } = parseFile(testPath); + const expected = 6; + const result = navigateNetworkSimultaneously(steps, rules); + + if (result !== expected) { + throw new Error(`Test failed: expected ${expected}, got ${result}`); + } + + console.log(`Test passed with ${result} steps.`); +} + +function main(): void { + try { + runTest(); + const inputPath = path.join(__dirname, '../input.txt'); + const { steps, rules } = parseFile(inputPath); + const result = navigateNetworkSimultaneously(steps, rules); + console.log(`All paths reached 'Z' nodes simultaneously in ${result} steps.`); + } catch (error: any) { + console.error(`Error: ${error.message}`); + } +} + +main(); diff --git a/package-lock.json b/package-lock.json index e975788..c9e5b1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@types/node": "^20.10.4", "fs": "^0.0.1-security" }, "devDependencies": { @@ -156,6 +157,14 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/node": { + "version": "20.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", + "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/@types/semver": { "version": "7.5.6", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", @@ -1519,6 +1528,11 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index 6c0ffbf..a1d441c 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "eslint": "^8.55.0" }, "dependencies": { + "@types/node": "^20.10.4", "fs": "^0.0.1-security" } } diff --git a/tsconfig.json b/tsconfig.json index 301024d..cbb70b6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "ES2023", "module": "commonjs", "strict": true, "esModuleInterop": true