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> = 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) => row.slice()); mapClone[robotPos.y][robotPos.x] = '@'; console.log(mapClone.map((row: Array) => 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 = 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();