From 25f3706e0faced0b16ed5afe06da876228f49acb Mon Sep 17 00:00:00 2001 From: Pascal Syma Date: Sun, 15 Dec 2024 16:11:37 +0100 Subject: [PATCH] Solve 2024-15 --- 2024/15/2024-15.run.xml | 7 + 2024/15/index.test.ts | 55 +++++++ 2024/15/index.ts | 351 ++++++++++++++++++++++++++++++++++++++++ 2024/15/run.ts | 12 ++ utils/array.ts | 2 +- 5 files changed, 426 insertions(+), 1 deletion(-) create mode 100644 2024/15/2024-15.run.xml create mode 100644 2024/15/index.test.ts create mode 100644 2024/15/index.ts create mode 100644 2024/15/run.ts diff --git a/2024/15/2024-15.run.xml b/2024/15/2024-15.run.xml new file mode 100644 index 0000000..bf25c84 --- /dev/null +++ b/2024/15/2024-15.run.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/2024/15/index.test.ts b/2024/15/index.test.ts new file mode 100644 index 0000000..24f4689 --- /dev/null +++ b/2024/15/index.test.ts @@ -0,0 +1,55 @@ +import { describe, expect, it } from 'bun:test'; +import { solveFirst, solveSecond } from './index.ts'; + +describe('2024-15', () => { + const testInput = `########## +#..O..O.O# +#......O.# +#.OO..O.O# +#..O@..O.# +#O#..O...# +#O..O..O.# +#.OO.O.OO# +#....O...# +########## + +^v>^vv^v>v<>v^v<<><>>v^v^>^<<<><^ +vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<^<^^>>>^<>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^v^^<^^vv< +<>^^^^>>>v^<>vvv^>^^^vv^^>v<^^^^v<>^>vvvv><>>v^<<^^^^^ +^><^><>>><>^^<<^^v>>><^^>v>>>^v><>^v><<<>vvvv>^<><<>^>< +^>><>^v<><^vvv<^^<><^v<<<><<<^^<^>>^<<<^>>^v^>>^v>vv>^<<^v<>><<><<>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^ +<><^^>^^^<>^vv<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<> +^^>vv<^v^v^<>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<>< +v^^>>><<^^<>>^v^v^<<>^<^v^v><^<<<><<^vv>>v>v^<<^`; + it('first', () => { + expect( + solveFirst(`######## +#..O.O.# +##@.O..# +#...O..# +#.#.O..# +#...O..# +#......# +######## + +<^^>>>vv>v<<`) + ).toBe(2028); + expect(solveFirst(testInput)).toBe(10092); + }); + it('second', () => { + expect( + solveSecond(`####### +#...#.# +#.....# +#..OO@# +#..O..# +#.....# +####### + +') { + if (right === '#') { + continue; + } + if (right === '.') { + x++; + continue; + } + if (right === 'O') { + let firstFree = 2; + while (firstFree + x < width) { + if (map[y][firstFree + x] === '#') { + continue outer; + } + if (map[y][firstFree + x] === '.') { + break; + } + firstFree++; + } + if (firstFree + x >= width) { + continue; + } + replaceChar(map, x + 1, y, '.'); + replaceChar(map, x + firstFree, y, 'O'); + + x++; + continue; + } + } else if (instruction === 'v') { + if (down === '#') { + continue; + } + if (down === '.') { + y++; + continue; + } + + if (down === 'O') { + let firstFree = 2; + while (firstFree + y < height) { + if (map[firstFree + y][x] === '#') { + continue outer; + } + if (map[firstFree + y][x] === '.') { + break; + } + firstFree++; + } + if (firstFree + y >= height) { + continue; + } + replaceChar(map, x, y + 1, '.'); + replaceChar(map, x, y + firstFree, 'O'); + + y++; + continue; + } + } else if (instruction === '<') { + if (left === '#') { + continue; + } + if (left === '.') { + x--; + continue; + } + if (left === 'O') { + let firstFree = -2; + while (firstFree + x > 0) { + if (map[y][firstFree + x] === '#') { + continue outer; + } + if (map[y][firstFree + x] === '.') { + break; + } + firstFree--; + } + if (firstFree + x <= 0) { + continue; + } + replaceChar(map, x - 1, y, '.'); + replaceChar(map, x + firstFree, y, 'O'); + + x--; + continue; + } + } else if (instruction === '^') { + if (top === '#') { + continue; + } + if (top === '.') { + y--; + continue; + } + + if (top === 'O') { + let firstFree = -2; + while (firstFree + y > 0) { + if (map[firstFree + y][x] === '#') { + continue outer; + } + if (map[firstFree + y][x] === '.') { + break; + } + firstFree--; + } + if (firstFree + y <= 0) { + continue; + } + replaceChar(map, x, y - 1, '.'); + replaceChar(map, x, y + firstFree, 'O'); + + y--; + continue; + } + } + console.log('ERROR!'); + break; + } + + // console.log(map.join('\n')); + + const boxes = findCoordsOfDigit(map, 'O'); + + return sumArray(boxes.map(([x, y]) => 100 * y + x)); +} + +export function solveSecond(input: string): number { + const [rawMap, rawInstructions] = input.split('\n\n'); + const map = rawMap.split('\n').map((row) => + row + .split('') + .map((d) => (d === 'O' ? '[]' : d === '@' ? '@.' : d + d)) + .join('') + ); + const instructions = rawInstructions.split('\n').join(''); + + // console.log(map); + // console.log(instructions); + + const width = map[0].length; + + let [[x, y]] = findCoordsOfDigit(map, '@'); + + replaceChar(map, x, y, '.'); + + outer: for (const instruction of instructions) { + if (map.some((r) => r.includes('[[') || r.includes(']]'))) { + console.log('ERROR!'); + replaceChar(map, x, y, '@'); + break; + } + + // console.log(map.join('\n')); + // console.log(instruction, x, y); + const { left, right, down, top } = getAdjacent(map, x, y); + + if (instruction === '>') { + if (right === '#') { + continue; + } + if (right === '.') { + x++; + continue; + } + + if (right === '[') { + let firstFree = 3; + while (firstFree + x < width) { + if (map[y][firstFree + x] === '#') { + continue outer; + } + if (map[y][firstFree + x] === '.') { + break; + } + firstFree++; + } + if (firstFree + x >= width) { + continue; + } + const boxes = map[y].substring(x + 1, x + firstFree); + replaceChar(map, x + 2, y, boxes); + replaceChar(map, x + 1, y, '.'); + + x++; + continue; + } + } else if (instruction === 'v') { + if (down === '#') { + continue; + } + if (down === '.') { + y++; + continue; + } + const toMove: Coordinates = []; + const toMoveDigit: string[] = []; + if (down === ']') { + toMove.push([x, y + 1], [x - 1, y + 1]); + toMoveDigit.push(']', '['); + } else if (down === '[') { + toMove.push([x, y + 1], [x + 1, y + 1]); + toMoveDigit.push('[', ']'); + } + for (let i = 0; i < toMove.length; i++) { + const [mX, mY] = toMove[i]; + const { down: mDown } = getAdjacent(map, mX, mY); + if (!mDown) { + continue; + } + if (mDown === '[') { + toMove.push([mX, mY + 1], [mX + 1, mY + 1]); + toMoveDigit.push('[', ']'); + } else if (mDown === ']') { + toMove.push([mX - 1, mY + 1], [mX, mY + 1]); + toMoveDigit.push('[', ']'); + } + } + // TODO: filter unique?? + // console.log(toMove); + + if (toMove.some(([mX, mY]) => map[mY + 1][mX] === '#')) { + continue; + } + // console.log('move', 1); + toMove.forEach(([mX, mY], i) => + replaceChar(map, mX, mY + 1, toMoveDigit[i]) + ); + toMove.forEach( + ([mX, mY]) => + !toMove.some((c) => isCoordinateEqual(c, [mX, mY - 1])) && + replaceChar(map, mX, mY, '.') + ); + y++; + continue; + } else if (instruction === '<') { + if (left === '#') { + continue; + } + if (left === '.') { + x--; + continue; + } + if (left === ']') { + let firstFree = -3; + while (firstFree + x > 0) { + if (map[y][firstFree + x] === '#') { + continue outer; + } + if (map[y][firstFree + x] === '.') { + break; + } + firstFree--; + } + if (firstFree + x <= 0) { + continue; + } + const boxes = map[y].substring(x + firstFree + 1, x); + replaceChar(map, x + firstFree, y, boxes); + replaceChar(map, x - 1, y, '.'); + + x--; + continue; + } + } else if (instruction === '^') { + if (top === '#') { + continue; + } + if (top === '.') { + y--; + continue; + } + const toMove: Coordinates = []; + const toMoveDigit: string[] = []; + if (top === ']') { + toMove.push([x, y - 1], [x - 1, y - 1]); + toMoveDigit.push(']', '['); + } else if (top === '[') { + toMove.push([x, y - 1], [x + 1, y - 1]); + toMoveDigit.push('[', ']'); + } + + for (let i = 0; i < toMove.length; i++) { + const [mX, mY] = toMove[i]; + const { top: mTop } = getAdjacent(map, mX, mY); + if (!mTop) { + continue; + } + if (mTop === '[') { + toMove.push([mX, mY - 1], [mX + 1, mY - 1]); + toMoveDigit.push('[', ']'); + } else if (mTop === ']') { + toMove.push([mX - 1, mY - 1], [mX, mY - 1]); + toMoveDigit.push('[', ']'); + } + } + // TODO: filter unique?? + // console.log(toMove); + + if (toMove.some(([mX, mY]) => map[mY - 1][mX] === '#')) { + continue; + } + // console.log('move', 1); + toMove.forEach(([mX, mY], i) => + replaceChar(map, mX, mY - 1, toMoveDigit[i]) + ); + toMove.forEach( + ([mX, mY]) => + !toMove.some((c) => isCoordinateEqual(c, [mX, mY + 1])) && + replaceChar(map, mX, mY, '.') + ); + y--; + continue; + } + console.log('ERROR!'); + break; + } + + // console.log(map.join('\n')); + + const boxes = findCoordsOfDigit(map, /\[/g); + + return sumArray(boxes.map(([x, y]) => 100 * y + x)); +} diff --git a/2024/15/run.ts b/2024/15/run.ts new file mode 100644 index 0000000..88783cd --- /dev/null +++ b/2024/15/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/array.ts b/utils/array.ts index cdcc2b8..93f0fd5 100644 --- a/utils/array.ts +++ b/utils/array.ts @@ -135,5 +135,5 @@ export function unique( * @param char Char */ export function replaceChar(arr: string[], x: number, y: number, char: string) { - arr[y] = arr[y].substring(0, x) + char + arr[y].substring(x + 1); + arr[y] = arr[y].substring(0, x) + char + arr[y].substring(x + char.length); }