diff --git a/2021/12/part1.mjs b/2021/12/part1.mjs new file mode 100644 index 0000000..8767932 --- /dev/null +++ b/2021/12/part1.mjs @@ -0,0 +1,51 @@ +import fs from "fs-extra"; +import fp from "lodash/fp.js"; +import _ from "lodash"; + +import Graph from 'graphology'; + + + +export const processInput = (filename) => fs.readFile(filename,'utf8').then( + content => content.split("\n").filter(x=>x) +).then( + lines => { + const graph = new Graph(); + lines.map( line => line.split('-') ).forEach( edge => + graph.mergeEdge(...edge) ); + return graph; + } + +); + +export function solution(graph) { + let paths = 0; + const left = [ + { graph, where: 'start' } + ]; + + while( left.length > 0 ) { + const position = left.shift(); + + if( position.where === 'end' ) { + paths++; + continue; + } + + let newGraph = position.graph; + if( position.where === position.where.toLowerCase() ) { + newGraph = position.graph.copy(); + newGraph.dropNode(position.where); + } + + for( const n of position.graph.neighbors(position.where) ) { + left.unshift({ + graph: newGraph, + where: n, + }); + } + + } + + return paths; +} diff --git a/2021/12/part2.mjs b/2021/12/part2.mjs new file mode 100644 index 0000000..8dfa908 --- /dev/null +++ b/2021/12/part2.mjs @@ -0,0 +1,60 @@ +import fs from "fs-extra"; +import fp from "lodash/fp.js"; +import _ from "lodash"; + +import * as p1 from "./part1.mjs"; + +export function solution(graph) { + let paths = 0; + + graph.forEachNode((node) => { + graph.setNodeAttribute(node, "nbrVisits", 0); + }); + + const left = [{ graph, where: "start" }]; + + while (left.length > 0) { + const position = left.shift(); + + if (position.where === "end") { + paths++; + continue; + } + + const canVisit = (graph, node) => { + if (node === "start") return false; + if (node === node.toUpperCase()) return true; + const nbrVisits = graph.getNodeAttribute(node, "nbrVisits"); + + if (nbrVisits === 0) return true; + if (nbrVisits > 1) return false; + return !graph.hasAttribute("revisited"); + }; + + for (const n of position.graph + .neighbors(position.where) + .filter((n) => canVisit(position.graph, n))) { + let newGraph = position.graph; + + if (n === n.toLowerCase()) { + newGraph = newGraph.copy(); + newGraph.updateNodeAttribute( + n, + "nbrVisits", + fp.add(1) + ); + + if (newGraph.getNodeAttribute(n, "nbrVisits") === 2) { + newGraph.setAttribute("revisited", n); + } + } + + left.unshift({ + graph: newGraph, + where: n, + }); + } + } + + return paths; +} diff --git a/2021/12/sample b/2021/12/sample new file mode 100644 index 0000000..6fd8c41 --- /dev/null +++ b/2021/12/sample @@ -0,0 +1,7 @@ +start-A +start-b +A-c +A-b +b-d +A-end +b-end diff --git a/2021/12/test.mjs b/2021/12/test.mjs new file mode 100644 index 0000000..196547b --- /dev/null +++ b/2021/12/test.mjs @@ -0,0 +1,20 @@ +// https://adventofcode.com/2021/day/12 + +import tap from "tap"; +import fs from "fs-extra"; + +import * as p1 from "./part1.mjs"; +import * as p2 from "./part2.mjs"; + +const sample = p1.processInput('sample'); +const input = p1.processInput('input'); + +tap.test("part1", async (t) => { + t.equal(p1.solution(await sample), 10); + t.equal(p1.solution(await input), 3450); +}); + +tap.test("part2", async (t) => { + t.equal(p2.solution(await sample), 36); + t.equal(p2.solution(await input), 96528); +});