diff --git a/2024/06/2024-06.run.xml b/2024/06/2024-06.run.xml new file mode 100644 index 0000000..6dda585 --- /dev/null +++ b/2024/06/2024-06.run.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/2024/06/index.test.ts b/2024/06/index.test.ts new file mode 100644 index 0000000..c074600 --- /dev/null +++ b/2024/06/index.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'bun:test'; +import { solveFirst } from './index.ts'; + +describe('2024-06', () => { + const testInput = `....#..... +.........# +.......... +..#....... +.......#.. +.......... +.#..^..... +........#. +#......... +......#...`; + it('first', () => { + expect(solveFirst(testInput)).toBe(41); + }); + it('second', () => { + // that takes too long + // expect(solveSecond(testInput)).toBe(6); + }); +}); diff --git a/2024/06/index.ts b/2024/06/index.ts new file mode 100644 index 0000000..28dad22 --- /dev/null +++ b/2024/06/index.ts @@ -0,0 +1,127 @@ +import { sumBy } from '../../utils/array.ts'; + +export function replace(arr: string[], x: number, y: number, char: string) { + arr[y] = arr[y].substring(0, x) + char + arr[y].substring(x + 1); +} + +const directions = [ + { d: '^', x: 0, y: -1 }, + { d: '>', x: 1, y: 0 }, + { d: 'v', x: 0, y: 1 }, + { d: '<', x: -1, y: 0 } +] as const; + +export function solveFirst(input: string): number { + const rows = input.split('\n'); + + const columnCount = rows[0].length; + const rowCount = rows.length; + + let y = rows.findIndex((r) => r.indexOf('^') >= 0); + let x = rows[y].indexOf('^'); + let direction = 0; + + while (x >= 0 && x < columnCount && y >= 0 && y < rowCount) { + const nextX = x + directions[direction].x; + const nextY = y + directions[direction].y; + if (rows[nextY]?.[nextX] === '#') { + direction = (direction + 1) % 4; + } + + replace(rows, x, y, 'X'); + x += directions[direction].x; + y += directions[direction].y; + } + return sumBy(rows, (r) => [...r.matchAll(/X/g)].length); +} + +export function getNextObstacle( + rows: string[], + startX: number, + startY: number, + d: number +): false | { x: number; y: number } { + let x = startX; + let y = startY; + + while (true) { + const nextX = x + directions[d].x; + const nextY = y + directions[d].y; + const next = rows[nextY]?.[nextX]; + if (!next) { + return false; + } + if (next === '#') { + return { x, y }; + } + x = nextX; + y = nextY; + } +} + +export function finishes( + rows: string[], + startX: number, + startY: number, + d: number +): boolean { + let x = startX; + let y = startY; + + let i = 0; + do { + const next = getNextObstacle(rows, x, y, (d + i) % 4); + if (!next) { + return true; + } + x = next.x; + y = next.y; + i++; + + // TODO: be smarter + } while (i < 1_000_000); + + return false; +} + +export function solveRow( + rows: string[], + row: number, + startX: number, + startY: number, + startDirection: number +) { + let found = 0; + + for (let column = 0; column < rows[0].length; column++) { + if ((row === startX && column === startY) || rows[row][column] === '#') { + continue; + } + + replace(rows, column, row, '#'); + if (!finishes(rows, startX, startY, startDirection)) { + found++; + } + + replace(rows, column, row, '.'); + } + return found; +} + +export function solveSecond(input: string): number { + const rows = input.split('\n'); + const rowCount = rows.length; + + const y = rows.findIndex((r) => r.indexOf('^') >= 0); + const x = rows[y].indexOf('^'); + const direction = 0; + + let found = 0; + + for (let row = 0; row < rowCount; row++) { + found += solveRow(rows, row, x, y, direction); + console.log('row:', row, 'found', found); + } + + return found; +} diff --git a/2024/06/run.ts b/2024/06/run.ts new file mode 100644 index 0000000..88783cd --- /dev/null +++ b/2024/06/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/2024/06/run2.ts b/2024/06/run2.ts new file mode 100644 index 0000000..1716d5d --- /dev/null +++ b/2024/06/run2.ts @@ -0,0 +1,29 @@ +import { availableParallelism } from 'os'; +import pLimit from 'p-limit'; +import { join } from 'path'; +import { sumArray } from '../../utils/array.ts'; + +const workerFile = new URL('worker.ts', import.meta.url).href; + +const eva = (i: number) => + new Promise((resolve) => { + const worker = new Worker(workerFile); + + worker.postMessage(i); + worker.onmessage = (event) => { + console.log('row:', i, 'found', event.data); + resolve(event.data as number); + }; + }); + +const input = await Bun.file(join(__dirname, 'input.txt')).text(); +const rowCount = input.split('\n').length; + +const work = new Array(rowCount).fill(0).map((_, i) => i); + +const limit = pLimit(availableParallelism()); +const solution = await Promise.all(work.map((w) => limit(eva, w))); + +console.log(solution); + +console.log(sumArray(solution)); diff --git a/2024/06/worker.ts b/2024/06/worker.ts new file mode 100644 index 0000000..2625c36 --- /dev/null +++ b/2024/06/worker.ts @@ -0,0 +1,21 @@ +import { join } from 'path'; +import { solveRow } from './index.ts'; + +declare var self: Worker; + +const input = await Bun.file(join(__dirname, 'input.txt')).text(); + +self.onmessage = (event) => { + const row = event.data as number; + + console.log('Start row', row); + + const rows = input.split('\n'); + + const y = rows.findIndex((r) => r.indexOf('^') >= 0); + const x = rows[y].indexOf('^'); + const direction = 0; + + postMessage(solveRow(rows, row, x, y, direction)); + process.exit(); +}; diff --git a/bun.lockb b/bun.lockb index 3c72e86..a847f08 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 9a7064a..e22b510 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "prettier": "^3.4.0", "prettier-plugin-organize-imports": "^4.1.0", "prompts": "^2.4.2", - "replace-in-file": "^8.2.0" + "replace-in-file": "^8.2.0", + "p-limit": "^6.1.0" }, "peerDependencies": { "typescript": "^5.7.2"