Files
AoC/2024/16/index.ts

168 lines
4.8 KiB
TypeScript
Raw Normal View History

2024-12-16 22:01:40 +00:00
const fs = require('fs');
const input = fs.readFileSync(__dirname + '/input.txt', 'utf8');
interface Coord {
x: number,
y: number,
};
enum Dir {
Up,
Right,
Down,
Left,
};
const map: Array<Array<string>> = input.split("\n").map((row: string) => row.split(''));
const width = map[0].length;
const height = map.length;
const snakeIndex = input.indexOf('S');
let snakePos = { x: snakeIndex % (width + 1), y: Math.floor(snakeIndex / height) };
let snakeDir = Dir.Right
function get(pos: Coord): string {
return map[pos.y][pos.x];
}
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 printMap() {
const mapClone = map.slice().map((row: Array<string>) => row.slice());
console.log(mapClone.map((row: Array<string>) => row.join('')).join("\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 {
const adjs = [Dir.Up, Dir.Right, Dir.Down, Dir.Left].map((dir) => get(nextCoord(pos, dir))).join('');
return adjs === '###.'
|| adjs === '##.#'
|| adjs === '#.##'
|| adjs === '.###';
}
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 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);
}
}
};
}
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
const pos = { x, y };
const char = get(pos);
if (char !== '.') {
continue;
}
if (isDeadEnd(pos)) {
fillDeadEnd(pos);
}
}
}
let fastest: number|null = null;
const scoreCache: { [key: string]: [number, Coord, Dir] } = {};
2024-12-16 22:23:04 +00:00
const branchCache: { [key: string]: number } = {};
const scores: Array<[number, Array<string>]> = [];
2024-12-16 22:01:40 +00:00
function getScoresAfterPosition(pos: Coord, dir: Dir, currentScore = 0, path: Array<string> = [], lastBranch: [number, Coord]|null = null): void {
if (fastest !== null && currentScore >= fastest) {
return;
}
const branch = isBranch(pos, dir);
if (branch) {
2024-12-16 22:23:04 +00:00
const cacheKey = `${pos.x},${pos.y},${dir}`;
const cache = branchCache[cacheKey];
if (cache && cache < currentScore) {
return;
} else {
branchCache[cacheKey] = currentScore;
}
2024-12-16 22:01:40 +00:00
if (lastBranch) {
scoreCache[`${lastBranch[1].x},${lastBranch[1].y}`] = [currentScore - lastBranch[0], pos, dir];
}
lastBranch = [currentScore, pos];
}
[dir, nextDir(dir), prevDir(dir)].forEach((newDir, i) => {
const next = nextCoord(pos, newDir);
const nextStr = `${next.x},${next.y}`;
if (!path.includes(nextStr)) {
const add = i === 0 ? 1 : 1001;
const score = currentScore + add;
const nextChar = get(next);
if (nextChar === 'E') {
if (fastest === null || score < fastest) {
fastest = score;
}
lastBranch = null;
2024-12-16 22:23:04 +00:00
scores.push([score, [...path, nextStr]]);
2024-12-16 22:01:40 +00:00
return;
} else if (nextChar === '.') {
getScoresAfterPosition(next, newDir, score, [...path, nextStr], branch ? [score, next] : lastBranch);
}
}
});
return;
}
getScoresAfterPosition(snakePos, snakeDir);
2024-12-16 22:23:04 +00:00
const fastestScore = Math.min(...scores.map(([score]) => score));
let coordsOnPaths: Set<string> = new Set();
scores.forEach(([score, path]) => {
if (score === fastestScore) {
path.forEach((coord) => coordsOnPaths.add(coord));
}
});
console.log(coordsOnPaths.size + 1);