diff --git a/2023/day08/part2.ts b/2023/day08/part2.ts index 2ed5325..f9699c1 100644 --- a/2023/day08/part2.ts +++ b/2023/day08/part2.ts @@ -1,71 +1,99 @@ -type Node = { L: string; R: string }; - +type Node = { L: string; R: string; stepsToZCache: Record }; type Network = Record; - type Path = { - possiblePaths: string[]; + checkpoint: string; + sinceCheckpoint: number; + zsSeen: string[]; + current: string; }; -// - -export function answer(input: string) { - const [instructions, , ...networkRaw] = input.split("\n"); - - const parseNetwork = (lines: string[]): Network => - lines.reduce((network, line) => { - const [id, leftAndRight] = line.split(" = "); - const [L, R] = leftAndRight.replaceAll(/[\(\)]/g, "").split(", "); - return { ...network, [id]: { L, R } }; - }, {}); - - const network: Network = parseNetwork(networkRaw); - - // build the paths to X for each starting path - // step forward by longest path - // new positions are stepSize % pathSize - - const paths = Object.keys(network) - .filter((n) => n.endsWith("A")) - .map((p) => { - const path = buildPath(p, network, instructions); - console.log(`plength: ${path.length}, last: ${path[path.length - 1]}`); - return { current: path.length - 1, path }; - }); - - const stepSize = Math.max(...paths.map(({ path }) => path.length)); - let stepTotal = stepSize; - - while (!paths.every((p) => p.path[p.current] === "Z")) { - console.log(`step by ${stepSize}`); - paths.forEach((p) => { - p.current += stepSize; - p.current = p.path.length % p.current; - console.log(p); - }); - stepTotal += stepSize; - } +// for every node in the network, calculate the steps + +export function answer(input: string): number { + const [instructionsRaw, , ...networkRaw] = input.split("\n"); + const instructions = instructionsRaw.split(""); + + const network: Network = networkRaw.reduce((network, line) => { + const [id, leftAndRight] = line.split(" = "); + const [L, R] = leftAndRight.replaceAll(/[\(\)]/g, "").split(", "); + return { ...network, [id]: { L, R, stepsToZCache: {} } }; + }, {}); + + const cacheKey = (instIdx: number, endNode: string) => + `${endNode}.${instIdx}`; + + const allPaths: Path[] = Object.keys(network).map((n) => ({ + checkpoint: n, + sinceCheckpoint: 0, + current: n, + zsSeen: [], + })); + + walkPaths(allPaths, instructions, network); + + // const startPaths: Path[] = Object.keys(network) + // .filter((n) => n.endsWith("A")) + // .map((n) => ({ checkpoint: n, sinceCheckpoint: 0, current: n, zsSeen: [] })); + + // const endNodes = Object.keys(network).filter((n) => n.endsWith("Z")); - return stepTotal; + // MATH?!!! + // - permutations of `stepsToZ` + // - what operation can I perform with these numbers to know that it is the right answer? + + return 1; } -function buildPath( - start: string, - network: Network, - instructions: string, -): string[] { - let currentNode = start; - let stepCount = 0; - const endingChars = [start[2]]; - while (!currentNode.endsWith("Z")) { - for (const dir of instructions.split("")) { - currentNode = network[currentNode][dir as keyof Node]; - stepCount++; - endingChars.push(currentNode[2]); - if (currentNode.endsWith("Z")) break; +function walkPaths(paths: Path[], instructions: string[], network: Network) { + const endNodes = Object.keys(network).filter((n) => n.endsWith("Z")); + + let step = -1; + while (true) { + step++; + for (let instIdx = 0; instIdx < instructions.length; instIdx++) { + paths.forEach((p, _pathIdx) => { + p.sinceCheckpoint++; + + p.current = network[p.current][instructions[instIdx] as "L" | "R"]; + + if (p.current.endsWith("Z")) { + console.log( + `found path to z! ${p.checkpoint} from inst ${instIdx} takes ${p.sinceCheckpoint} steps`, + ); + network[p.checkpoint].stepsToZCache[`${instIdx}`] = p.sinceCheckpoint; + + p.checkpoint = p.current; + p.sinceCheckpoint = 0; + } + }); + + // const stepsToZ = paths + // .map((p) => + // endNodes.map( + // (n) => network[p.current].stepsToZCache[`${n}.${instIdx}`], + // ), + // ) + // .flat(); + const stepsToZ = paths.map( + (p) => network[p.current].stepsToZCache[`${instIdx}`], + ); + + console.log( + Object.keys(network).map( + (n) => `${n}: ${JSON.stringify(network[n].stepsToZCache)}`, + ), + ); + + if ( + Object.values(network).every( + (n) => Object.values(n.stepsToZCache).length === endNodes.length, + ) + ) { + return; + // return stepsToZ.reduce((product, x) => (product *= x), 1); + } } } - - return endingChars; } if (import.meta.main) { diff --git a/2023/day08/part2_slow.ts b/2023/day08/part2_slow.ts new file mode 100644 index 0000000..653c99e --- /dev/null +++ b/2023/day08/part2_slow.ts @@ -0,0 +1,48 @@ +type Node = { L: string; R: string }; +type Network = Record; + +export function answer(input: string): number { + const [instructions, , ...networkRaw] = input.split("\n"); + + const network: Network = networkRaw.reduce((network, line) => { + const [id, leftAndRight] = line.split(" = "); + const [L, R] = leftAndRight.replaceAll(/[\(\)]/g, "").split(", "); + return { ...network, [id]: { L, R } }; + }, {}); + + const pathsFinished = (paths: string[]): boolean => + paths.every((p) => p.endsWith("Z")); + + const step = (paths: string[], dir: keyof Node): string[] => + paths.map((c) => network[c][dir]); + + let paths = Object.keys(network).filter((n) => n.endsWith("A")); + let stepCount = 0; + + while (!pathsFinished(paths)) { + for (const dir of instructions.split("")) { + paths = step(paths, dir as keyof Node); + stepCount++; + if (pathsFinished(paths)) break; + } + } + + return stepCount; +} + +function log(paths: string[], step: number) { + console.log( + `${stepCount.toString().padStart(20, "0")}: ${paths + .map((p, i) => (p.endsWith("Z") ? i : ".")) + .join("")}`, + ); +} + +if (import.meta.main) { + const input = ( + await Deno.readFile("input").then((bytes) => + new TextDecoder().decode(bytes), + ) + ).trim(); + console.log(answer(input)); +}