diff --git a/06/index.ts b/06/index.ts index aa6b917..13507a0 100644 --- a/06/index.ts +++ b/06/index.ts @@ -1,37 +1,55 @@ import fs from "node:fs"; import { printTime, now } from "../util"; -type tile = { guard: boolean; blocked: boolean; visited: boolean }; +type tile = { + guard: boolean; + blocked: boolean; + visited: boolean; + hit: { north: number; east: number; south: number; west: number }; +}; -const test = true; +const test = false; const data = fs.readFileSync( test ? "./inputs/testinput" : "./inputs/input", "utf8" ); +const generateFloor = (data: string): Record => { + return data + .split("\n") + .slice(0, -1) + .reduce((floor, row, i) => { + row.split("").forEach((v, col) => { + floor[`${i}|${col}`] = { + guard: v === "^", + blocked: v === "#", + visited: false, + hit: { + north: 0, + south: 0, + east: 0, + west: 0, + }, + }; + }); + return floor; + }, {} as Record); +}; + +const keyToCoord = (string): [number, number] => { + return string.split("|").map((v) => parseInt(v, 10)); +}; + let timer = now.instant(); console.log( "part one:", (() => { - let floor = data - .split("\n") - .slice(0, -1) - .reduce((floor, row, i) => { - row.split("").forEach((v, col) => { - floor[`${i}|${col}`] = { - guard: v === "^", - blocked: v === "#", - visited: false, - }; - }); - return floor; - }, {} as Record); - + let floor = generateFloor(data); let direction = "north"; let [guard] = Object.entries(floor).filter((tile) => tile[1].guard)[0]; while (true) { - let [x, y] = guard.split("|").map((v) => parseInt(v, 10)); + let [x, y] = keyToCoord(guard); floor[guard].visited = true; let next = ""; switch (direction) { @@ -73,3 +91,128 @@ console.log( })(), printTime(now.instant().since(timer)) ); + +timer = now.instant(); +console.log( + "part two:", + (() => { + const floor = generateFloor(data); + + let direction = "north"; + let guard = Object.entries(floor).filter((tile) => tile[1].guard)[0][0]; + const initialGuardPosition = guard; + while (true) { + let [x, y] = keyToCoord(guard); + floor[guard].visited = true; + let next = ""; + switch (direction) { + case "north": + next = `${x - 1}|${y}`; + break; + case "south": + next = `${x + 1}|${y}`; + break; + case "east": + next = `${x}|${y + 1}`; + break; + case "west": + next = `${x}|${y - 1}`; + break; + } + if (floor[next] === undefined) { + // guard left the floor, exit + break; + } + if (!floor[next].blocked) { + // move the guard + floor[next].guard = true; + floor[guard].guard = false; + guard = next; + } else { + // guard is blocked, rotate + if (direction === "north") { + direction = "east"; + } else if (direction === "east") { + direction = "south"; + } else if (direction === "south") { + direction = "west"; + } else if (direction === "west") { + direction = "north"; + } + } + } + let newObstructions = Object.entries(floor).reduce((total, tile) => { + if (tile[1].visited && tile[0] !== initialGuardPosition) { + return [...total, tile[0]]; + } + return total; + }, [] as string[]); + return newObstructions.reduce((total, curr) => { + const floor = generateFloor(data); + floor[curr].blocked = true; + + // iterate as above, but this time we tract how many times the guard hits any obstruction from the same direction. + // if the guard ever hits one from the same direction twice, we know they're in a loop. + let direction = "north"; + let guard = Object.entries(floor).filter((tile) => tile[1].guard)[0][0]; + while (true) { + let [x, y] = keyToCoord(guard); + let next = ""; + switch (direction) { + case "north": + next = `${x - 1}|${y}`; + break; + case "south": + next = `${x + 1}|${y}`; + break; + case "east": + next = `${x}|${y + 1}`; + break; + case "west": + next = `${x}|${y - 1}`; + break; + } + if (floor[next] === undefined) { + // guard left the floor, exit + break; + } + if (!floor[next].blocked) { + // move the guard + floor[next].guard = true; + floor[guard].guard = false; + guard = next; + } else { + // guard is blocked, log impact and rotate + if (direction === "north") { + floor[next].hit.north++; + if (floor[next].hit.north === 2) { + return total + 1; + } + direction = "east"; + } else if (direction === "east") { + floor[next].hit.east++; + if (floor[next].hit.east === 2) { + return total + 1; + } + direction = "south"; + } else if (direction === "south") { + floor[next].hit.south++; + if (floor[next].hit.south === 2) { + return total + 1; + } + direction = "west"; + } else if (direction === "west") { + floor[next].hit.west++; + if (floor[next].hit.west === 2) { + return total + 1; + } + direction = "north"; + } + } + } + + return total; + }, 0); + })(), + printTime(now.instant().since(timer)) +);