diff --git a/2025/07/2025-07.run.xml b/2025/07/2025-07.run.xml new file mode 100644 index 0000000..99b7111 --- /dev/null +++ b/2025/07/2025-07.run.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/2025/07/index.test.ts b/2025/07/index.test.ts new file mode 100644 index 0000000..c6b3718 --- /dev/null +++ b/2025/07/index.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from 'bun:test'; +import { solveFirst, solveSecond } from './index.ts'; + +describe('2025-07', () => { + const testInput = `.......S....... +............... +.......^....... +............... +......^.^...... +............... +.....^.^.^..... +............... +....^.^...^.... +............... +...^.^...^.^... +............... +..^...^.....^.. +............... +.^.^.^.^.^...^. +...............`; + it('first', () => { + expect(solveFirst(testInput)).toBe(21); + }); + it('second', () => { + expect(solveSecond(testInput)).toBe(40); + }); +}); diff --git a/2025/07/index.ts b/2025/07/index.ts new file mode 100644 index 0000000..d01875f --- /dev/null +++ b/2025/07/index.ts @@ -0,0 +1,51 @@ +import { MultiMap } from '../../utils/multi-map.ts'; + +export function solveFirst(input: string): number { + const rows = input.split('\n'); + const start = rows[0].indexOf('S'); + + const beams = new Set([start]); + + let splits = 0; + for (let y = 1; y < rows.length; y++) { + const newBeams: number[] = []; + beams.forEach((beam) => { + if (rows[y][beam] === '.' || beam >= rows[y].length) { + return; + } + if (rows[y][beam] === '^') { + splits++; + beams.delete(beam); + newBeams.push(beam - 1); + newBeams.push(beam + 1); + } + }); + newBeams.forEach((b) => beams.add(b)); + } + + return splits; +} + +export function solveSecond(input: string): number { + const rows = input.split('\n'); + const start = rows[0].indexOf('S'); + + return solveDown(rows, 1, start); +} + +const cache = new MultiMap(); +function solveDown(map: string[], startY: number, startX: number): number { + if (cache.multiHas(startY, startX)) { + return cache.multiGet(startY, startX)!; + } + for (let y = startY; y < map.length; y++) { + if (map[y][startX] === '^') { + return cache.multiSet( + solveDown(map, y + 1, startX - 1) + solveDown(map, y + 1, startX + 1), + startY, + startX + ); + } + } + return 1; +} diff --git a/2025/07/run.ts b/2025/07/run.ts new file mode 100644 index 0000000..88783cd --- /dev/null +++ b/2025/07/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/utils/multi-map.ts b/utils/multi-map.ts index 5d85e59..e729c6b 100644 --- a/utils/multi-map.ts +++ b/utils/multi-map.ts @@ -11,6 +11,18 @@ export class MultiMap extends Map { } return this.get(firstKey) as V; } + multiHas(...keys: K): boolean { + const firstKey = keys[0]; + const otherKeys = keys.slice(1); + if (!this.has(firstKey)) { + return false; + } + + if (otherKeys.length > 0) { + return (this.get(firstKey)! as MultiMap).multiHas(...otherKeys); + } + return this.has(firstKey); + } multiSet(value: V, ...keys: K) { const firstKey = keys[0];