134 lines
3.1 KiB
TypeScript
134 lines
3.1 KiB
TypeScript
|
|
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 obs: Array<Coord> = [];
|
||
|
|
let guard: { pos: Coord, dir: Dir } = { pos: { x: 0, y: 0}, dir: Dir.Up };
|
||
|
|
|
||
|
|
const keyMap: { [key: string]: (pos: Coord) => any } = {
|
||
|
|
"#": (pos: Coord) => obs.push(pos),
|
||
|
|
"^": (pos: Coord) => guard = { pos, dir: Dir.Up },
|
||
|
|
">": (pos: Coord) => guard = { pos, dir: Dir.Up },
|
||
|
|
"v": (pos: Coord) => guard = { pos, dir: Dir.Up },
|
||
|
|
"<": (pos: Coord) => guard = { pos, dir: Dir.Up },
|
||
|
|
}
|
||
|
|
|
||
|
|
let x = 0;
|
||
|
|
let y = 0;
|
||
|
|
for (let i = 0; i <= input.length; i++) {
|
||
|
|
const char = input[i];
|
||
|
|
if (char === "\n") {
|
||
|
|
y++;
|
||
|
|
x = 0;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (keyMap[char]) {
|
||
|
|
keyMap[char]({ x, y });
|
||
|
|
}
|
||
|
|
x++;
|
||
|
|
}
|
||
|
|
const width = x + 1;
|
||
|
|
const height = y + 1;
|
||
|
|
const guardStart = guard.pos;
|
||
|
|
|
||
|
|
|
||
|
|
const positions: Array<Coord> = [guard.pos];
|
||
|
|
|
||
|
|
function coodsMatch(a: Coord, b: Coord) {
|
||
|
|
return a.x === b.x && a.y === b.y;
|
||
|
|
}
|
||
|
|
|
||
|
|
function getNextPosition() {
|
||
|
|
if (guard.dir === Dir.Up) {
|
||
|
|
return { x: guard.pos.x, y: guard.pos.y - 1 };
|
||
|
|
}
|
||
|
|
if (guard.dir === Dir.Right) {
|
||
|
|
return { x: guard.pos.x + 1, y: guard.pos.y };
|
||
|
|
}
|
||
|
|
if (guard.dir === Dir.Down) {
|
||
|
|
return { x: guard.pos.x, y: guard.pos.y + 1 };
|
||
|
|
}
|
||
|
|
return { x: guard.pos.x - 1, y: guard.pos.y };
|
||
|
|
}
|
||
|
|
|
||
|
|
function rotateGuard() {
|
||
|
|
if (guard.dir === Dir.Up) {
|
||
|
|
guard.dir = Dir.Right;
|
||
|
|
} else if (guard.dir === Dir.Right) {
|
||
|
|
guard.dir = Dir.Down;
|
||
|
|
} else if (guard.dir === Dir.Down) {
|
||
|
|
guard.dir = Dir.Left;
|
||
|
|
} else {
|
||
|
|
guard.dir = Dir.Up;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function isObs(coord: Coord, otherObs: Array<Coord>|null = null) {
|
||
|
|
return (otherObs || obs).some((obsCoord) => coodsMatch(obsCoord, coord));
|
||
|
|
}
|
||
|
|
|
||
|
|
function offMap(coord: Coord) {
|
||
|
|
return coord.x >= width
|
||
|
|
|| coord.x < 0
|
||
|
|
|| coord.y >= height
|
||
|
|
|| coord.y < 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
function alreadyVisited(coord: Coord) {
|
||
|
|
return positions.some((pos) => coodsMatch(pos, coord));
|
||
|
|
}
|
||
|
|
|
||
|
|
while (true) {
|
||
|
|
const nextPos = getNextPosition();
|
||
|
|
if (isObs(nextPos)) {
|
||
|
|
rotateGuard();
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (offMap(nextPos)) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
guard.pos = nextPos;
|
||
|
|
if (!alreadyVisited(nextPos)) {
|
||
|
|
positions.push(nextPos);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Part 2
|
||
|
|
let loops = 0;
|
||
|
|
positions.slice(1).forEach((pos, i) => {
|
||
|
|
console.log(Math.floor(`${i / positions.length * 100}%`));
|
||
|
|
guard.pos = guardStart;
|
||
|
|
guard.dir = Dir.Up;
|
||
|
|
const newObs = [...obs, pos];
|
||
|
|
const guardPositions: Array<string> = [];
|
||
|
|
while (true) {
|
||
|
|
const nextPos = getNextPosition();
|
||
|
|
if (isObs(nextPos, newObs)) {
|
||
|
|
rotateGuard();
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (offMap(nextPos)) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
const guardPos = `${guard.pos.x},${guard.pos.y},${guard.dir}`;
|
||
|
|
if (guardPositions.includes(guardPos)) {
|
||
|
|
loops++;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
guardPositions.push(guardPos);
|
||
|
|
guard.pos = nextPos;
|
||
|
|
}
|
||
|
|
x++;
|
||
|
|
});
|
||
|
|
// End Part 2
|
||
|
|
|
||
|
|
console.log(positions.length);
|
||
|
|
console.log(loops);
|