diff --git a/2024/18/2024-18.run.xml b/2024/18/2024-18.run.xml new file mode 100644 index 0000000..c8a06bb --- /dev/null +++ b/2024/18/2024-18.run.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/2024/18/index.test.ts b/2024/18/index.test.ts new file mode 100644 index 0000000..9d033ce --- /dev/null +++ b/2024/18/index.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'bun:test'; +import { solveFirst, solveSecond } from './index.ts'; + +describe('2024-18', () => { + const testInput = `5,4 +4,2 +4,5 +3,0 +2,1 +6,3 +2,4 +1,5 +0,6 +3,3 +2,6 +5,1 +1,2 +5,5 +2,5 +6,5 +1,4 +0,4 +6,4 +1,1 +6,1 +1,0 +0,5 +1,6 +2,0`; + it('first', () => { + expect(solveFirst(testInput, 7, 7, 12)).toBe(22); + }); + it('second', () => { + expect(solveSecond(testInput, 7, 7, 12)).toBe('6,1'); + }); +}); diff --git a/2024/18/index.ts b/2024/18/index.ts new file mode 100644 index 0000000..daeee86 --- /dev/null +++ b/2024/18/index.ts @@ -0,0 +1,167 @@ +// @ts-ignore +import { astar, Graph } from 'javascript-astar'; +import { replaceChar } from '../../utils/array.ts'; +import { + type Coordinate, + inBounds, + isCoordinateEqual +} from '../../utils/coordinates.ts'; +import { MultiMap } from '../../utils/multi-map.ts'; +import { PriorityQueue } from '../../utils/queue.ts'; + +function getGraph( + allBytes: [number, number][], + byteCount: number, + height: number, + width: number +): Graph { + const bytes = allBytes.slice(0, byteCount); + + const map = new Array(height) + .fill(null) + .map((_, y) => + new Array(width) + .fill(0) + .map((_, x) => + bytes.findIndex((c) => isCoordinateEqual(c, [x, y])) >= 0 ? 0 : 1 + ) + ); + + return new Graph(map); +} + +export function solveFirst( + input: string, + width = 71, + height = 71, + byteCount = 1024 +): number { + const match = input.matchAll(/^(\d*),(\d*)/gm); + + const allBytes = Array.from( + match, + (m) => m.slice(1, 3).map(Number) as Coordinate + ); + const graph = getGraph(allBytes, byteCount, height, width); + + const start = graph.grid[0][0]; + const end = graph.grid[height - 1][width - 1]; + + const result = astar.search(graph, start, end); + return result.length; +} + +export function solveSecond( + input: string, + width = 71, + height = 71, + byteCount = 1024 +): string { + const match = input.matchAll(/^(\d*),(\d*)/gm); + + const allBytes = Array.from( + match, + (m) => m.slice(1, 3).map(Number) as Coordinate + ); + + // let lastPath: Coordinates = []; + for (let i = byteCount; i < allBytes.length; i++) { + const nextByte = allBytes[i]; + // if ( + // lastPath.length > 0 && + // !lastPath.some((p) => isCoordinateEqual(p, nextByte)) + // ) { + // console.log(lastPath); + // continue; + // } + + const graph = getGraph(allBytes, i + 1, height, width); + const start = graph.grid[0][0]; + + const end = graph.grid[height - 1][width - 1]; + const result = astar.search(graph, start, end); + if (result.length <= 0) { + return nextByte.join(','); + } + // lastPath = result.map((n: GridNode) => [n.x, n.y] as Coordinate); + } + return ''; +} + +export function solveFirstOld( + input: string, + width = 71, + height = 71, + byteCount = 1024 +): number { + const match = input.matchAll(/^(\d*),(\d*)/gm); + + const bytes = Array.from( + match, + (m) => m.slice(1, 3).map(Number) as Coordinate + ).slice(0, byteCount); + + const map = new Array(height) + .fill(null) + .map(() => new Array(width).fill('.').join('') as string); + + bytes.forEach(([x, y]) => replaceChar(map, x, y, '#')); + replaceChar(map, width - 1, height - 1, 'E'); + + console.log(map.join('\n')); + const start = { x: 0, y: 0, score: 0 }; + const possibleOptions = new PriorityQueue(); + + possibleOptions.enqueue(start, 0); + const visited = new Set<`${number}:${number}`>(); + const scores = new MultiMap(); + visited.add(`${start.x}:${start.y}`); + + let i = 0; + while (possibleOptions.size() > 0) { + const el = possibleOptions.dequeue()!!; + + scores.multiSet(el.node.score, el.node.y, el.node.x); + visited.add(`${el.node.x}:${el.node.y}`); + if (map[el.node.y]?.[el.node.x] === 'E') { + // console.log(visited); + return el.node.score; + } + if (possibleOptions.size() % 10_000 === 0) { + console.log(visited.size, possibleOptions.size()); + } + for (let [x, y] of [ + [0, 1], + [0, -1], + [1, 0], + [-1, 0] + ]) { + const nx = el.node.x + x; + const ny = el.node.y + y; + const score = el.node.score + 1; + if (!inBounds(width, height)([nx, ny]) || map[ny]?.[nx] === '#') { + continue; + } + if (visited.has(`${nx}:${ny}`)) { + continue; + } + const existingScore = scores.multiGet(ny, nx) || Number.MAX_SAFE_INTEGER; + if (score < existingScore) { + possibleOptions.enqueue({ x: nx, y: ny, score }, score); + } + } + // console.log('a', el.node, visited.size, possibleOptions.size()); + // return 0; + i++; + + if (i >= 10_000_000) { + visited.forEach((v) => { + const [x, y] = v.split(':').map(Number); + replaceChar(map, x, y, 'O'); + }); + console.log(map.join('\n')); + return 0; + } + } + return 0; +} diff --git a/2024/18/run.ts b/2024/18/run.ts new file mode 100644 index 0000000..88783cd --- /dev/null +++ b/2024/18/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/bun.lockb b/bun.lockb index a847f08..24b152f 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index d7bb010..4c76cca 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ }, "devDependencies": { "@types/bun": "latest", + "@types/javascript-astar": "^0.0.35", "@types/prompts": "^2.4.9", "p-limit": "^6.1.0", "prettier": "^3.4.0", @@ -21,5 +22,8 @@ }, "peerDependencies": { "typescript": "^5.7.2" + }, + "dependencies": { + "javascript-astar": "^0.4.1" } }