Files
advent-of-code/2024/day-15/day15-part2.ts
2024-12-15 20:23:54 +01:00

365 lines
9.3 KiB
TypeScript

import { logLines } from "../../utils/log-lines.ts"
const keypress = async () => {
process.stdin.setRawMode(true)
return new Promise((resolve) =>
process.stdin.once("data", (data) => {
const byteArray = [...data]
if (byteArray.length > 0 && byteArray[0] === 3) {
console.log("^C")
process.exit(1)
}
process.stdin.setRawMode(false)
resolve()
}),
)
}
enum Move {
Up = "^",
Down = "v",
Right = ">",
Left = "<",
}
enum Unit {
Box = "O",
Robot = "@",
Wall = "#",
Empty = ".",
}
enum LargeUnit {
BoxLeft = "[",
BoxRight = "]",
}
type Grid = (Unit | LargeUnit)[][]
type Coordinates = [number, number]
export async function part2(input: string) {
const { moves, grid } = parseInput(input)
for (const move of moves) {
console.log(`Moving ${move}`)
logGrid(grid)
await keypress()
try {
const robotPosition = getRobotStartingPosition(grid)
canExecuteMove(robotPosition, grid, move)
executeMove(grid, move, robotPosition, Unit.Robot)
} catch (e) {
// console.log("error", e.message)
}
}
logGrid(grid)
return getGPS(grid)
}
export function parseInput(input: string) {
const [gridStr, movesStr] = input.split("\n\n")
const moves = movesStr
.split("\n")
.join("")
.split("")
.map((move) => {
if (move === Move.Up) {
return Move.Up
}
if (move === Move.Down) {
return Move.Down
}
if (move === Move.Right) {
return Move.Right
}
if (move === Move.Left) {
return Move.Left
}
throw new Error(`Invalid input: ${move}`)
})
const grid = gridStr.split("\n").map((line) =>
line.split("").flatMap((u) => {
if (u === Unit.Box) {
return [LargeUnit.BoxLeft, LargeUnit.BoxRight]
}
if (u === Unit.Empty) {
return [Unit.Empty, Unit.Empty]
}
if (u === Unit.Robot) {
return [Unit.Robot, Unit.Empty]
}
if (u === Unit.Wall) {
return [Unit.Wall, Unit.Wall]
}
throw new Error(`Invalid input unit: ${u}`)
}),
)
return { moves, grid }
}
export function getRobotStartingPosition(grid: Grid): Coordinates {
for (let i = 0; i < grid.length; i++) {
const line = grid[i]
for (let i1 = 0; i1 < line.length; i1++) {
const unit = line[i1]
if (unit === Unit.Robot) {
return [i, i1]
}
}
}
throw new Error("Robot not found")
}
export function canExecuteMove(
position: Coordinates,
grid: Grid,
move: Move,
): Coordinates {
// console.log("CAN EXECUTE?: ")
// logGrid(grid)
const { unit: nextItem, coordinates: nextItemCoordinates } = getNextItem(
position,
grid,
move,
)
const currentUnit = grid[position[0]][position[1]]
if (!nextItem) {
throw new Error("wtf")
}
if (nextItem === Unit.Wall) {
throw new Error("Wall found, can't execute move sequence.")
}
if (nextItem === Unit.Empty) {
return nextItemCoordinates
}
if (move === Move.Up || move === Move.Down) {
if (currentUnit === Unit.Robot) {
if (nextItem === LargeUnit.BoxRight) {
const checkRight = canExecuteMove(nextItemCoordinates, grid, move)
const checkLeft = canExecuteMove(
[nextItemCoordinates[0], nextItemCoordinates[1] - 1],
grid,
move,
)
return
}
if (nextItem === LargeUnit.BoxLeft) {
const checkRight = canExecuteMove(
[nextItemCoordinates[0], nextItemCoordinates[1] + 1],
grid,
move,
)
const checkLeft = canExecuteMove(nextItemCoordinates, grid, move)
return
}
}
if (currentUnit === LargeUnit.BoxRight) {
if (nextItem === LargeUnit.BoxLeft) {
const checkUp = canExecuteMove(nextItemCoordinates, grid, move)
const checkRight = canExecuteMove(
[nextItemCoordinates[0], nextItemCoordinates[1] + 1],
grid,
move,
)
return
}
}
if (currentUnit === LargeUnit.BoxLeft) {
if (nextItem === LargeUnit.BoxRight) {
const checkUp = canExecuteMove(nextItemCoordinates, grid, move)
const checkLeft = canExecuteMove(
[nextItemCoordinates[0], nextItemCoordinates[1] - 1],
grid,
move,
)
return
}
}
}
return canExecuteMove(nextItemCoordinates, grid, move)
}
export function executeMove(
grid: Grid,
move: Move,
currentCoordinates: Coordinates,
currentUnit: Unit | LargeUnit,
) {
if (move === Move.Down) {
const [cr, cc] = currentCoordinates
const [nr, nc] = [cr + 1, cc]
const nextUnit = grid[nr][nc]
if (currentUnit === Unit.Robot && nextUnit === LargeUnit.BoxLeft) {
grid[nr][nc] = currentUnit
grid[nr][nc + 1] = Unit.Empty
grid[cr][cc] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc + 1], LargeUnit.BoxRight)
} else if (currentUnit === Unit.Robot && nextUnit === LargeUnit.BoxRight) {
grid[nr][nc] = currentUnit
grid[nr][nc - 1] = Unit.Empty
grid[cr][cc] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc - 1], LargeUnit.BoxLeft)
} else if (
currentUnit === LargeUnit.BoxLeft &&
nextUnit === LargeUnit.BoxRight
) {
grid[nr][nc] = currentUnit
grid[nr][nc - 1] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc - 1], LargeUnit.BoxLeft)
} else if (
currentUnit === LargeUnit.BoxRight &&
nextUnit === LargeUnit.BoxLeft
) {
grid[nr][nc] = currentUnit
grid[nr][nc + 1] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc + 1], LargeUnit.BoxRight)
} else if (nextUnit === Unit.Empty) {
if (currentUnit === Unit.Robot) {
grid[cr][cc] = Unit.Empty
}
grid[nr][nc] = currentUnit
return
} else if (
currentUnit === LargeUnit.BoxRight &&
nextUnit === LargeUnit.BoxRight
) {
grid[nr][nc] = currentUnit
grid[nr][nc - 1] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc - 1], LargeUnit.BoxLeft)
} else if (
currentUnit === LargeUnit.BoxLeft &&
nextUnit === LargeUnit.BoxLeft
) {
grid[nr][nc] = currentUnit
grid[nr][nc + 1] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc + 1], LargeUnit.BoxRight)
}
} else if (move === Move.Up) {
const [cr, cc] = currentCoordinates
const [nr, nc] = [cr - 1, cc]
const nextUnit = grid[nr][nc]
if (currentUnit === Unit.Robot && nextUnit === LargeUnit.BoxLeft) {
grid[nr][nc] = currentUnit
grid[nr][nc + 1] = Unit.Empty
grid[cr][cc] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc + 1], LargeUnit.BoxRight)
} else if (currentUnit === Unit.Robot && nextUnit === LargeUnit.BoxRight) {
grid[nr][nc] = currentUnit
grid[nr][nc - 1] = Unit.Empty
grid[cr][cc] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc - 1], LargeUnit.BoxLeft)
} else if (
currentUnit === LargeUnit.BoxLeft &&
nextUnit === LargeUnit.BoxRight
) {
grid[nr][nc] = currentUnit
grid[nr][nc - 1] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc - 1], LargeUnit.BoxLeft)
} else if (
currentUnit === LargeUnit.BoxRight &&
nextUnit === LargeUnit.BoxLeft
) {
grid[nr][nc] = currentUnit
grid[nr][nc + 1] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc + 1], LargeUnit.BoxRight)
} else if (nextUnit === Unit.Empty) {
if (currentUnit === Unit.Robot) {
grid[cr][cc] = Unit.Empty
}
grid[nr][nc] = currentUnit
return
} else if (
currentUnit === LargeUnit.BoxRight &&
nextUnit === LargeUnit.BoxRight
) {
grid[nr][nc] = currentUnit
grid[nr][nc - 1] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc - 1], LargeUnit.BoxLeft)
} else if (
currentUnit === LargeUnit.BoxLeft &&
nextUnit === LargeUnit.BoxLeft
) {
grid[nr][nc] = currentUnit
grid[nr][nc + 1] = Unit.Empty
executeMove(grid, move, [nr, nc], nextUnit)
executeMove(grid, move, [nr, nc + 1], LargeUnit.BoxRight)
}
} else if (move === Move.Right) {
const [cr, cc] = currentCoordinates
const [nr, nc] = [cr, cc + 1]
const nextUnit = grid[nr][nc]
grid[nr][nc] = currentUnit
if (currentUnit === Unit.Robot) {
grid[cr][cc] = Unit.Empty
}
if (nextUnit === Unit.Empty) {
return
}
executeMove(grid, move, [nr, nc], nextUnit)
} else if (move === Move.Left) {
const [cr, cc] = currentCoordinates
const [nr, nc] = [cr, cc - 1]
const nextUnit = grid[nr][nc]
grid[nr][nc] = currentUnit
if (currentUnit === Unit.Robot) {
grid[cr][cc] = Unit.Empty
}
if (nextUnit === Unit.Empty) {
return
}
executeMove(grid, move, [nr, nc], nextUnit)
} else throw new Error(`wrong move: ${move}`)
}
function getNextItem([r, c]: Coordinates, grid: Grid, move: Move) {
let coordinates: Coordinates
if (move === Move.Left) {
coordinates = [r, c - 1]
} else if (move === Move.Right) {
coordinates = [r, c + 1]
} else if (move === Move.Up) {
coordinates = [r - 1, c]
} else if (move === Move.Down) {
coordinates = [r + 1, c]
} else {
throw new Error("Invalid input for getNextItem")
}
const unit = grid[coordinates[0]][coordinates[1]] as
| Unit
| LargeUnit
| undefined
return { unit, coordinates }
}
export function getGPS(grid: Grid) {
let GPS = 0
for (let i = 0; i < grid.length; i++) {
const line = grid[i]
for (let i1 = 0; i1 < line.length; i1++) {
const unit = line[i1]
if (unit === LargeUnit.BoxLeft) {
GPS += i * 100 + i1
}
}
}
return GPS
}
function logGrid(grid: Grid) {
logLines(grid.map((x) => x.join("")))
}