UNPKG

expeditaet

Version:
126 lines (111 loc) 3.86 kB
/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { BoundingBox, DOUBLE_NEWLINE, Direction, GridGraphNode, NEWLINE, mapFirst, task, zip, type Vec2String, } from '@alexaegis/advent-of-code-lib'; import packageJson from '../package.json'; import { CubeMap } from './cube-map.class.js'; import { getFacingScore, parseMovementInstructions } from './parse.function.js'; export type Vec2EdgeString = `${Vec2String}-${Vec2String}`; const getShortestSegment = (input: string): number => input .split(NEWLINE) .map((line) => [...line.matchAll(/ +/g), ...line.matchAll(/[#.]+/g)] .map((matches) => matches[0].length) .min(), ) .min(); export const p2 = (input: string): number => { const [map, rawInstructions] = input.splitIntoStringPair(DOUBLE_NEWLINE); const instructions = parseMovementInstructions(rawInstructions); const graph = map.toGridGraph(); const sideLength = getShortestSegment(input); const flatCubeBox = graph.boundingBox(); const horizontalFaceCount = flatCubeBox.horizontal.length / sideLength; const verticalFaceCount = flatCubeBox.vertical.length / sideLength; const flatCubeFaceBox = BoundingBox.fromSize({ x: horizontalFaceCount, y: verticalFaceCount }); const flatCube = flatCubeFaceBox.createBlankMatrix((flatCubeCell) => { const topLeft = flatCubeCell.clone().applyChange((n) => n * sideLength); if (graph.nodes.has(topLeft.toString())) { const bottomRight = flatCubeCell.clone().applyChange((n) => (n + 1) * sideLength - 1); return BoundingBox.fromVectors([topLeft, bottomRight]); } else { return undefined; } }); const cube = new CubeMap(flatCube); cube.solve(); // For every cubeface in the simplistic representation where faces are already connected for (const cubeFace of cube.cubeGraph.nodes.values()) { // get the box of the face on the flat representation const faceBox = cubeFace.value; // Then in every direction for (const direction of Direction.cardinalDirections) { // Then for the source edge, get all the coordinates for the nodes along that edge const sourceEdge = faceBox .getEdge(direction) .map((coordinate) => graph.getNode(coordinate)!); // Find the matching edge on the target cubeface const targetCubeface = cubeFace.neighbours.get(direction)!.to; const [reverseDirection] = [...targetCubeface.neighbours.entries()].find( ([, edge]) => edge.to === cubeFace, )!; // And collect the edge of the target face const targetEdge = targetCubeface.value .getEdge(reverseDirection) .map((coordinate) => graph.getNode(coordinate)!) .reverse(); for (const [from, to] of zip(sourceEdge, targetEdge)) { from.neighbours.set(direction, { from, to, data: reverseDirection.turn(180), weight: from.value === to.value ? 0 : Number.POSITIVE_INFINITY, direction, }); } } } const start = mapFirst(flatCubeBox.horizontal.collectValues(), (x) => { const node = graph.getNode({ x, y: 0 }); return node?.value === '.' ? node : undefined; })!; const path: GridGraphNode[] = [start]; let position = start; let direction = Direction.EAST; for (const instruction of instructions) { if (instruction === 'L') { direction = direction.left(); } else if (instruction === 'R') { direction = direction.right(); } else { for (let i = 0; i < instruction; i++) { const edge = position.neighbours.get(direction); if (edge && edge.weight !== Number.POSITIVE_INFINITY) { position = edge.to; path.push(position); if (edge.data) { direction = edge.data as Direction; } } } } } if (process.env['RUN']) { graph.printPath(path); } return ( 1000 * (position.coordinate.y + 1) + 4 * (position.coordinate.x + 1) + getFacingScore(direction) ); }; await task(p2, packageJson.aoc); // 120175 ~63.39ms