const fs = require('fs'); const input = fs.readFileSync(__dirname + '/input.txt', 'utf8').split("\n"); interface Coord { x: number, y: number, }; enum Dir { Up, Right, Down, Left, }; const map: Array> = []; const width = 71; const height = 71; for (let y = 0; y < height; y++) { if (!map[y]) { map[y] = []; } for (let x = 0; x < width; x++) { map[y][x] = '.'; } } for (let i = 0; i < 1024; i++) { const [x, y] = input[i].split(',').map((st: string) => parseInt(st)); map[y][x] = '#' } function get(pos: Coord): string { if (map[pos.y] && map[pos.y][pos.x]) { return map[pos.y][pos.x]; } return '#'; } function set(pos: Coord, val: string): void { map[pos.y][pos.x] = val; } function nextDir(dir: Dir): Dir { const directions = [Dir.Up, Dir.Right, Dir.Down, Dir.Left]; const i = (directions.indexOf(dir) + 1) % directions.length; return directions[i]; } function prevDir(dir: Dir): Dir { const directions = [Dir.Up, Dir.Right, Dir.Down, Dir.Left]; const i = directions.indexOf(dir) - 1; if (i < 0) { return directions[directions.length - 1]; } return directions[i]; } function isBranch(pos: Coord, dir: Dir): boolean { const leftDir = prevDir(dir); const rightDir = nextDir(dir); const next = nextCoord(pos, dir); const left = nextCoord(pos, leftDir); const right = nextCoord(pos, rightDir); const nextChar = get(next); const leftChar = get(left); const rightChar = get(right); return nextChar === '.' && leftChar === '.' || nextChar === '.' && rightChar === '.' || leftChar === '.' && rightChar === '.'; } function printMap(path: Array = []) { const mapClone = map.slice().map((row: Array) => row.slice()); path.forEach(({ x, y }) => { mapClone[y][x] = 'O'; }); console.log(mapClone.map((row: Array) => row.join('')).join("\n")); console.log("\n"); } function nextCoord(pos: Coord, direction: Dir): Coord { return direction === Dir.Up ? { x: pos.x, y: pos.y - 1 } : (direction === Dir.Right ? { x: pos.x + 1, y: pos.y } : (direction === Dir.Down ? { x: pos.x, y: pos.y + 1 } : { x: pos.x - 1, y: pos.y }) ); } function isDeadEnd(pos: Coord): boolean { if ((pos.x === 0 && pos.y === 0) || (pos.x === width - 1 && pos.y === width - 1)) { return false; } const adjs = [Dir.Up, Dir.Right, Dir.Down, Dir.Left].map((dir) => get(nextCoord(pos, dir))).join(''); return adjs === '###.' || adjs === '##.#' || adjs === '#.##' || adjs === '.###'; } function fillDeadEnd(pos: Coord): void { set(pos, '#') for (let dir of [Dir.Up, Dir.Right, Dir.Down, Dir.Left]) { const next = nextCoord(pos, dir); if (get(next) === '.') { if (isDeadEnd(next)) { fillDeadEnd(next); } } }; } printMap(); for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const pos = { x, y }; const char = get(pos); if (char !== '.') { continue; } if (isDeadEnd(pos)) { fillDeadEnd(pos); } } } printMap(); let fastest: number|null = null; const scoreCache: { [key: string]: [number, Coord, Dir] } = {}; const branchCache: { [key: string]: number } = {}; const scores: Array> = []; function getScoresAfterPosition(pos: Coord, dir: Dir, path: Array = [], lastBranch: [number, Coord]|null = null): void { if (fastest !== null && path.length >= fastest) { return; } const branch = isBranch(pos, dir); if (branch) { const cacheKey = `${pos.x},${pos.y},${dir}`; const cache = branchCache[cacheKey]; if (cache && cache < path.length) { return; } else { branchCache[cacheKey] = path.length; } lastBranch = [path.length, pos]; } [dir, nextDir(dir), prevDir(dir)].forEach((newDir, i) => { const next = nextCoord(pos, newDir); if (!path.find(({ x, y }) => next.x === x && next.y === y)) { if (next.x === width - 1 && next.y === height - 1) { if (fastest === null || path.length < fastest) { fastest = path.length; } scores.push(path); return; } else if (next.x >= 0 && next. x < width && next.y >= 0 && next.y < height && get(next) === '.') { getScoresAfterPosition(next, newDir, [...path, next], lastBranch); } } }); return; } getScoresAfterPosition({ x: 0, y: 0 }, Dir.Up, [{ x: 0, y: 0 }]); console.log(scores); const fastestScore = Math.min(...scores.map((p) => p.length)); const fastestPath = scores.find((p) => p.length === fastestScore); printMap(fastestPath); console.log(fastestScore);