index.js (4400B)
1 const fs = require('fs'); 2 const through = require('through2'); 3 const line_by_line = require('../common/line-for-line'); 4 5 String.prototype.startsWith = function(subject) { 6 return this.substr(0, subject.length) === subject; 7 }; 8 9 const valves = {}; 10 const map = {}; 11 const nmap = {}; 12 const rmap = []; 13 14 15 // Consistent-cost Dijkstra 16 function costFinder(from, to) { 17 const cost = Object.keys(valves) 18 .reduce((r, key) => { 19 r[key] = Infinity; 20 return r; 21 }, {}); 22 cost[from] = 0; 23 const openSet = [from]; 24 while(openSet.length) { 25 openSet.sort((a,b) => a.cost - b.cost); 26 const active = openSet.shift(); 27 for(const next of valves[active].tunnels) { 28 const nCost = cost[active]+1; 29 if (next == to) { 30 return nCost; 31 } 32 if (nCost < cost[next]) { 33 cost[next] = nCost; 34 openSet.push(next); 35 } 36 } 37 } 38 return cost[to] 39 } 40 41 fs.createReadStream('input') 42 .pipe(line_by_line()) 43 44 // Minor parsing 45 .pipe(through.obj(function(line, enc, cb) { 46 line = line.toString(); 47 if (!line) return cb(); 48 const valve = { state: false, total: 0 }; 49 const parts = line.split(' '); 50 parts.shift(); 51 valve.id = parts.shift(); 52 parts.shift(); 53 parts.shift(); 54 valve.rate = parseInt(parts.shift().replace(/[^\d\-]/g, '')); 55 parts.shift(); 56 parts.shift(); 57 parts.shift(); 58 parts.shift(); 59 valve.tunnels = parts.map(s => s.replace(/,/g,'')); 60 valves[valve.id] = valve; 61 cb(); 62 })) 63 64 .on('finish', () => { 65 console.log('loaded'); 66 67 // Precalculate cost between all valves 68 const COST = {}; 69 for(let src of Object.keys(valves)) { 70 for(let dst of Object.keys(valves)) { 71 COST[src+dst] = costFinder(src, dst); 72 } 73 } 74 75 // Remove non-interesting valves 76 for(const valve of Object.values(valves)) { 77 if(valve.id == 'AA') continue; 78 if (valve.rate) continue; 79 delete valves[valve.id]; 80 } 81 82 // Name to value map 83 let i = 1; 84 let targets = 0; 85 for(const name of Object.keys(valves)) { 86 nmap[name] = i; 87 rmap[i] = name; 88 targets += i; 89 i *= 2; 90 } 91 92 // Initialize solution finder 93 const totalTime = 26; 94 const actorCount = 2; 95 let maxPressure = 0; 96 let timer = 0; 97 let minlen = Infinity; 98 const openStates = [ 99 { 100 valves: nmap['AA'], 101 actors: new Array(actorCount).fill(0).map(() => ({ location: 'AA', time: 0 })), 102 pressure: 0, 103 } 104 ]; 105 106 map[nmap['AA'].toString(2)] = 0; 107 108 while(openStates.length) { 109 if (openStates.length < minlen) minlen = openStates.length; 110 if (--timer < 0) { timer = 1e5; console.log(minlen); minlen = Infinity; } 111 112 const state = openStates.pop(); 113 114 // Track max pressure released 115 if (state.pressure > maxPressure) { 116 maxPressure = state.pressure; 117 console.log({ maxPressure }); 118 } 119 120 // All valves opened, no further work on this path 121 if (state.valves == targets) { 122 continue; 123 } 124 125 for(const idx in state.actors) { 126 const actor = state.actors[idx]; 127 // Skip current actor if over-time 128 if (actor.time > totalTime) continue; 129 // Emulate move + valve open 130 for(i=1; i<targets; i*=2) { 131 // Don't attempt valves already opened 132 if (state.valves & i) continue; 133 // Calculate cost & result of moving+opening currently selected valve 134 const cost = COST[actor.location + rmap[i]] + 1; 135 const aCost = actor.time + cost; 136 const pressure = (totalTime - aCost) * valves[rmap[i]].rate; 137 const result = state.valves ^ i; 138 if (aCost > totalTime) continue; 139 // Push to open states 140 const newPressure = state.pressure + ((totalTime-aCost) * valves[rmap[i]].rate); 141 const newActors = [...state.actors]; 142 newActors[idx] = { 143 location: rmap[i], 144 time: aCost, 145 }; 146 147 const cacheKey = result.toString(2); 148 if ((!map[cacheKey]) || (newPressure > map[cacheKey])) { 149 map[cacheKey] = newPressure; 150 openStates.push({ 151 valves: result, 152 actors: newActors, 153 pressure: newPressure, 154 }); 155 } 156 } 157 } 158 159 } 160 161 console.log(''); 162 console.log({ maxPressure }); 163 });