import {anyCharOf, newline, rest, uniLetter, whitespace} from "parjs"; import {then, many, manyTill, exactly, between, manySepBy, stringify} from "parjs/combinators"; import fs from "fs"; import {CamelCards} from "./day_seven"; const patternParser = anyCharOf("LR").pipe(manyTill(newline().pipe(exactly(2)))); const nodeNameParser = uniLetter().pipe(exactly(3), stringify()); const childParser = nodeNameParser.pipe(manySepBy(", "), exactly(2), between("(", ")")); const nodeParser = nodeNameParser.pipe(then(childParser.pipe(between(" = ", whitespace())))) const parser = patternParser.pipe(then(nodeParser.pipe(manySepBy(whitespace())))); class Node { constructor( public label: string, public _left?: Node, public _right?: Node ) { if (this._left?.label === label) this._left = undefined; if (this._right?.label === label) this._right = undefined; } public set left(node: Node) { if (node.label !== this.label) { this._left = node; } } public set right(node: Node) { if (node.label !== this.label) { this._right = node; } } public get left(): Node | undefined { return this._left; } public get right(): Node | undefined { return this._right; } public get isBranch(): boolean { return Boolean(this.left || this.right); } /** * Find a node by label using basic Depth-first search */ public find(label: string, searchedNodes: string[] = []): Node | undefined { if (this.label === label) return this; if (searchedNodes.includes(this.label)) return; if (this.left) { const leftSearch = this.left.find(label, [...searchedNodes, this.label]); if (leftSearch) return leftSearch; } if (this.right) { const rightSearch = this.right.find(label, [...searchedNodes, this.label]); if (rightSearch) return rightSearch; } } } type Instruction = "L" | "R"; export class DesertMap { private readonly pattern: Instruction[]; private tree?: Node; constructor(input: string) { const [pattern, nodes] = parser.parse(input).value; this.pattern = pattern as Instruction[]; for (const [name, [[leftNode, rightNode]]] of nodes) { if (!this.tree) { this.tree = new Node(name); } const parent = this.tree.find(name); if (!parent) { console.log(`No parent found for label ${name}`) } else { const foundLeft = this.tree.find(leftNode); parent.left = foundLeft || new Node(leftNode); const foundRight = this.tree.find(rightNode); parent.right = foundRight || new Node(rightNode); } } } public stepsTo(node: string): number { if (!this.tree) return 0; let step = 0; let curr = this.tree; while (curr.label !== node) { const instruction = this.pattern[step % this.pattern.length]; if (instruction === "L" && curr.left) { curr = curr.left; } else if (instruction === "R" && curr.right) { curr = curr.right; } if (!curr) return 0; step++; } return step; } } export const runDayEight = () => { const input = fs.readFileSync('./inputs/day_eight_input.txt', 'utf-8').trimEnd(); const map = new DesertMap(input); console.log(map.stepsTo('ZZZ')); }