const fs = require('fs/promises'); const sum = (arr) => arr.reduce((total, num) => total+num, 0); const arrayOfLength = n => Array.from(Array(n).keys()).map(() => {}); const createNode = (str) => { const stats = str.match(/Valve ([A-Z]+) has flow rate=([\d]+); tunnels? leads? to valves? (.*)$/); if(!stats) { console.log("Bad line? : ", str); return; } const id = stats[1]; const rate = stats[2]; const edges = stats[3].split(', '); return { id, rate, edges, }; }; const main = async () => { const input = (await fs.readFile("./input.txt", 'utf8')).split("\n").slice(0, -1).sort(); const nodesById = {}; for(const row of input) { const node = createNode(row); if(!node) continue; nodesById[node.id] = node; } const nodesByRate = Object.values(nodesById).sort((a, b) => b.rate - a.rate).filter(n => n.rate > 0); console.log("nodes by rate: ", nodesByRate); const findEachOptimalPath = (node, queue = [], visited = {}, length = 0) => { visited[node.id] = length; for(const edge of node.edges) { const neighbor = nodesById[edge]; if(typeof visited[neighbor.id] === "undefined") { queue.push(() => findEachOptimalPath(neighbor, queue, visited, length + 1)); } }; if(queue.length) { return queue.shift()(); } return visited; }; const valvePossibilities = Object.values(nodesById).reduce((possibilities, node) => { possibilities[node.id] = findEachOptimalPath(node); return possibilities; }, {}); let best = 0; const plan = (node, value, opened = {}, time = 30) => { if(node.rate > 0 && time > 0) { time = time - 1; opened[node.id] = true; value = value + time * node.rate; } if(value > best) { best = value; console.log(`${best} is better!`); } const distances = valvePossibilities[node.id]; for(const next of nodesByRate.filter(n => !opened[n.id])) { const distance = distances[next.id]; if(time > distance + 1) { plan(next, value, {...opened}, time - distances[next.id]); } } }; plan(nodesById["AA"], 0); console.log("Answer is: ", best); } main();