1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
import fs from "fs";
function gcd(a: number, b: number) {
return !b ? a : gcd(b, a % b);
}
function lcm(a: number, b: number) {
return (a * b) / gcd(a, b);
}
const [directions, _, ...lines] = fs
.readFileSync("./input.txt")
.toString()
.split("\n")
.slice(0, -1);
const nodes = lines
.map((line) => {
const match = line.match(/(...) = \((...), (...)\)/);
if (!match) throw new Error("Could not match");
return {
node: match[1],
left: match[2],
right: match[3],
};
})
.map((node, index, nodes) => ({
name: node.node,
node: index,
left: nodes.findIndex((n) => n.node === node.left),
right: nodes.findIndex((n) => n.node === node.right),
}));
const [startingNodes, terminalNodes] = ["A", "Z"].map((char) =>
nodes.filter((n) => n.name.endsWith(char)).map((n) => n.node),
);
const result = startingNodes
.map((node) => {
let steps = 0;
while (!terminalNodes.includes(node)) {
const direction = (
{
L: "left",
R: "right",
} as const
)[directions[steps % directions.length]];
if (!direction) throw new Error("Invalid direction");
node = nodes[node][direction];
steps++;
}
return steps;
})
.reduce((acc, x) => lcm(acc, x), 1);
console.log({ result });
// Visualization into graphviz file
// $ neato graph.dot -Tpng -o out.png
const dotFile = "graph.dot";
fs.writeFileSync(dotFile, "digraph aoc {\n");
startingNodes.forEach((node) => {
fs.appendFileSync(dotFile, ` ${nodes[node].name} [color=green];\n`);
});
terminalNodes.forEach((node) => {
fs.appendFileSync(dotFile, ` ${nodes[node].name} [color=red];\n`);
});
nodes.forEach((node) => {
fs.appendFileSync(dotFile, ` ${node.name} -> ${nodes[node.left].name};\n`);
fs.appendFileSync(dotFile, ` ${node.name} -> ${nodes[node.right].name};\n`);
});
fs.appendFileSync(dotFile, "}\n");
|