Solve 2024-15
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="2024-15" type="BunRunConfiguration">
|
||||
<option name="program" value="2024/15/run.ts" />
|
||||
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -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...#
|
||||
##########
|
||||
|
||||
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
|
||||
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
|
||||
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
|
||||
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
|
||||
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
|
||||
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
|
||||
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
|
||||
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
|
||||
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
|
||||
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^`;
|
||||
it('first', () => {
|
||||
expect(
|
||||
solveFirst(`########
|
||||
#..O.O.#
|
||||
##@.O..#
|
||||
#...O..#
|
||||
#.#.O..#
|
||||
#...O..#
|
||||
#......#
|
||||
########
|
||||
|
||||
<^^>>>vv<v>>v<<`)
|
||||
).toBe(2028);
|
||||
expect(solveFirst(testInput)).toBe(10092);
|
||||
});
|
||||
it('second', () => {
|
||||
expect(
|
||||
solveSecond(`#######
|
||||
#...#.#
|
||||
#.....#
|
||||
#..OO@#
|
||||
#..O..#
|
||||
#.....#
|
||||
#######
|
||||
|
||||
<vv<<^^<<^^`)
|
||||
).toBe(105 + 306 + 207);
|
||||
expect(solveSecond(testInput)).toBe(9021);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,351 @@
|
||||
import { replaceChar, sumArray } from '../../utils/array.ts';
|
||||
import {
|
||||
type Coordinates,
|
||||
findCoordsOfDigit,
|
||||
getAdjacent,
|
||||
isCoordinateEqual
|
||||
} from '../../utils/coordinates.ts';
|
||||
|
||||
// TODO: cleanup??
|
||||
export function solveFirst(input: string): number {
|
||||
const [rawMap, rawInstructions] = input.split('\n\n');
|
||||
const map = rawMap.split('\n');
|
||||
const instructions = rawInstructions.split('\n').join('');
|
||||
|
||||
// console.log(map);
|
||||
// console.log(instructions);
|
||||
|
||||
const width = map[0].length;
|
||||
const height = map.length;
|
||||
|
||||
let [[x, y]] = findCoordsOfDigit(map, '@');
|
||||
|
||||
replaceChar(map, x, y, '.');
|
||||
|
||||
outer: for (const instruction of instructions) {
|
||||
// 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 === '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));
|
||||
}
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user