184 lines
5.0 KiB
TypeScript
184 lines
5.0 KiB
TypeScript
|
|
const fs = require('fs');
|
||
|
|
|
||
|
|
const input = fs.readFileSync(__dirname + '/input.txt', 'utf8');
|
||
|
|
|
||
|
|
interface Coord {
|
||
|
|
x: number,
|
||
|
|
y: number,
|
||
|
|
};
|
||
|
|
|
||
|
|
let [mapStr, movementsStr] = input.split("\n\n");
|
||
|
|
mapStr = mapStr.replace(/([#.])/g, '$1$1').replaceAll('O', '[]').replaceAll('@', '@.');
|
||
|
|
|
||
|
|
const map: Array<Array<string>> = mapStr.split("\n").map((row: string) => row.split(''))
|
||
|
|
const movements = movementsStr.split("\n").join('');
|
||
|
|
|
||
|
|
const width = map[0].length;
|
||
|
|
const height = map.length;
|
||
|
|
|
||
|
|
const robotIndex = mapStr.indexOf('@');
|
||
|
|
let robotPos = { x: robotIndex % (width + 1), y: Math.floor(robotIndex / width) };
|
||
|
|
map[robotPos.y][robotPos.x] = '.';
|
||
|
|
|
||
|
|
function nextCoord(pos: Coord, dir: '^'|'>'|'<'|'v'): Coord {
|
||
|
|
return {
|
||
|
|
"^": { x: pos.x, y: pos.y - 1 },
|
||
|
|
">": { x: pos.x + 1, y: pos.y },
|
||
|
|
"v": { x: pos.x, y: pos.y + 1 },
|
||
|
|
"<": { x: pos.x - 1, y: pos.y },
|
||
|
|
}[dir];
|
||
|
|
}
|
||
|
|
|
||
|
|
function printMap() {
|
||
|
|
const mapClone = map.slice().map((row: Array<string>) => row.slice());
|
||
|
|
mapClone[robotPos.y][robotPos.x] = '@';
|
||
|
|
console.log(mapClone.map((row: Array<string>) => row.join('')).join("\n"));
|
||
|
|
}
|
||
|
|
|
||
|
|
printMap();
|
||
|
|
|
||
|
|
function get(coord: Coord) {
|
||
|
|
return map[coord.y][coord.x];
|
||
|
|
}
|
||
|
|
|
||
|
|
function set(coord: Coord, val: string) {
|
||
|
|
return map[coord.y][coord.x] = val;
|
||
|
|
}
|
||
|
|
|
||
|
|
function canPushBox(leftPart: Coord, dir: '^'|'>'|'<'|'v'): boolean {
|
||
|
|
if (dir === '<') {
|
||
|
|
const next = nextCoord(leftPart, dir);
|
||
|
|
const val = get(next);
|
||
|
|
if (val === '#') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (val === ']') {
|
||
|
|
const movedNext = canPushBox(nextCoord(next, dir)!, dir);
|
||
|
|
if (!movedNext) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
const rightPart = nextCoord(leftPart, '>');
|
||
|
|
if (dir === '>') {
|
||
|
|
const next = nextCoord(rightPart, dir);
|
||
|
|
const val = get(next);
|
||
|
|
if (val === '#') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (val === '[') {
|
||
|
|
const movedNext = canPushBox(next, dir);
|
||
|
|
if (!movedNext) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
const nextLeft = nextCoord(leftPart, dir);
|
||
|
|
const nextRight = nextCoord(rightPart, dir);
|
||
|
|
const nextLeftVal = get(nextLeft);
|
||
|
|
const nextRightVal = get(nextRight);
|
||
|
|
if (nextLeftVal === '#' || nextRightVal === '#') {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (nextLeftVal === '[' || nextLeftVal === ']') {
|
||
|
|
const movedNext = canPushBox(nextLeftVal === '[' ? nextLeft : nextCoord(nextLeft, '<'), dir);
|
||
|
|
if (!movedNext) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (nextRightVal === '[') {
|
||
|
|
const movedNext = canPushBox(nextRight, dir);
|
||
|
|
if (!movedNext) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
function pushBox(leftPart: Coord, dir: '^'|'>'|'<'|'v') {
|
||
|
|
if (dir === '<') {
|
||
|
|
const next = nextCoord(leftPart, dir);
|
||
|
|
const val = get(next);
|
||
|
|
if (val === ']') {
|
||
|
|
pushBox(nextCoord(next, dir)!, dir);
|
||
|
|
}
|
||
|
|
set(next, '[');
|
||
|
|
set(leftPart, ']');
|
||
|
|
set(nextCoord(leftPart, '>')!, '.');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const rightPart = nextCoord(leftPart, '>');
|
||
|
|
if (dir === '>') {
|
||
|
|
const next = nextCoord(rightPart, dir);
|
||
|
|
const val = get(next);
|
||
|
|
if (val === '[') {
|
||
|
|
pushBox(next, dir);
|
||
|
|
}
|
||
|
|
set(rightPart, '[');
|
||
|
|
set(next, ']');
|
||
|
|
set(leftPart, '.');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const nextLeft = nextCoord(leftPart, dir);
|
||
|
|
const nextRight = nextCoord(rightPart, dir);
|
||
|
|
const nextLeftVal = get(nextLeft);
|
||
|
|
const nextRightVal = get(nextRight);
|
||
|
|
if (nextLeftVal === '[' || nextLeftVal === ']') {
|
||
|
|
pushBox(nextLeftVal === '[' ? nextLeft : nextCoord(nextLeft, '<'), dir);
|
||
|
|
}
|
||
|
|
if (nextRightVal === '[') {
|
||
|
|
pushBox(nextRight, dir);
|
||
|
|
}
|
||
|
|
set(nextLeft, '[');
|
||
|
|
set(nextRight, ']');
|
||
|
|
set(leftPart, '.');
|
||
|
|
set(rightPart, '.');
|
||
|
|
}
|
||
|
|
|
||
|
|
function sleep() {
|
||
|
|
const promise: Promise<void> = new Promise((resolve) => {
|
||
|
|
setTimeout(() => resolve(), 50);
|
||
|
|
});
|
||
|
|
return promise;
|
||
|
|
}
|
||
|
|
|
||
|
|
async function go() {
|
||
|
|
for (let i = 0; i < movements.length; i++) {
|
||
|
|
const direction = movements[i];
|
||
|
|
|
||
|
|
const nextRobotPosition = nextCoord(robotPos, direction);
|
||
|
|
let value = get(nextRobotPosition);
|
||
|
|
if (value === '#') {
|
||
|
|
printMap();
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (value === '[' || value === ']') {
|
||
|
|
const leftBoxCoord = value === '[' ? nextRobotPosition : nextCoord(nextRobotPosition, '<');
|
||
|
|
if (canPushBox(leftBoxCoord, direction)) {
|
||
|
|
pushBox(leftBoxCoord, direction);
|
||
|
|
} else {
|
||
|
|
printMap();
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
robotPos = nextRobotPosition;
|
||
|
|
printMap();
|
||
|
|
await sleep();
|
||
|
|
}
|
||
|
|
|
||
|
|
let sum = 0;
|
||
|
|
|
||
|
|
map.forEach((row, y) => {
|
||
|
|
row.forEach((val, x) => {
|
||
|
|
if (val === '[') {
|
||
|
|
sum += ((y * 100) + x);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log(sum);
|
||
|
|
}
|
||
|
|
|
||
|
|
go();
|