advent-of-code

Entries to advent of code, multiple years
git clone git://git.finwo.net/misc/advent-of-code
Log | Files | Refs

commit 23a6a2543bb57aeeb2a62445624e01af95b7145c
parent 96935fb8eb497d4d111e645ffc57b8e0792fea56
Author: finwo <finwo@pm.me>
Date:   Fri, 16 Dec 2022 17:17:02 +0100

2022/16 solution

Diffstat:
A2022/day-16/index.js | 163+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A2022/day-16/sample | 10++++++++++
2 files changed, 173 insertions(+), 0 deletions(-)

diff --git a/2022/day-16/index.js b/2022/day-16/index.js @@ -0,0 +1,163 @@ +const fs = require('fs'); +const through = require('through2'); +const line_by_line = require('../common/line-for-line'); + +String.prototype.startsWith = function(subject) { + return this.substr(0, subject.length) === subject; +}; + +const valves = {}; +const map = {}; +const nmap = {}; +const rmap = []; + + +// Consistent-cost Dijkstra +function costFinder(from, to) { + const cost = Object.keys(valves) + .reduce((r, key) => { + r[key] = Infinity; + return r; + }, {}); + cost[from] = 0; + const openSet = [from]; + while(openSet.length) { + openSet.sort((a,b) => a.cost - b.cost); + const active = openSet.shift(); + for(const next of valves[active].tunnels) { + const nCost = cost[active]+1; + if (next == to) { + return nCost; + } + if (nCost < cost[next]) { + cost[next] = nCost; + openSet.push(next); + } + } + } + return cost[to] +} + +fs.createReadStream('input') + .pipe(line_by_line()) + + // Minor parsing + .pipe(through.obj(function(line, enc, cb) { + line = line.toString(); + if (!line) return cb(); + const valve = { state: false, total: 0 }; + const parts = line.split(' '); + parts.shift(); + valve.id = parts.shift(); + parts.shift(); + parts.shift(); + valve.rate = parseInt(parts.shift().replace(/[^\d\-]/g, '')); + parts.shift(); + parts.shift(); + parts.shift(); + parts.shift(); + valve.tunnels = parts.map(s => s.replace(/,/g,'')); + valves[valve.id] = valve; + cb(); + })) + + .on('finish', () => { + console.log('loaded'); + + // Precalculate cost between all valves + const COST = {}; + for(let src of Object.keys(valves)) { + for(let dst of Object.keys(valves)) { + COST[src+dst] = costFinder(src, dst); + } + } + + // Remove non-interesting valves + for(const valve of Object.values(valves)) { + if(valve.id == 'AA') continue; + if (valve.rate) continue; + delete valves[valve.id]; + } + + // Name to value map + let i = 1; + let targets = 0; + for(const name of Object.keys(valves)) { + nmap[name] = i; + rmap[i] = name; + targets += i; + i *= 2; + } + + // Initialize solution finder + const totalTime = 26; + const actorCount = 2; + let maxPressure = 0; + let timer = 0; + let minlen = Infinity; + const openStates = [ + { + valves: nmap['AA'], + actors: new Array(actorCount).fill(0).map(() => ({ location: 'AA', time: 0 })), + pressure: 0, + } + ]; + + map[nmap['AA'].toString(2)] = 0; + + while(openStates.length) { + if (openStates.length < minlen) minlen = openStates.length; + if (--timer < 0) { timer = 1e5; console.log(minlen); minlen = Infinity; } + + const state = openStates.pop(); + + // Track max pressure released + if (state.pressure > maxPressure) { + maxPressure = state.pressure; + console.log({ maxPressure }); + } + + // All valves opened, no further work on this path + if (state.valves == targets) { + continue; + } + + for(const idx in state.actors) { + const actor = state.actors[idx]; + // Skip current actor if over-time + if (actor.time > totalTime) continue; + // Emulate move + valve open + for(i=1; i<targets; i*=2) { + // Don't attempt valves already opened + if (state.valves & i) continue; + // Calculate cost & result of moving+opening currently selected valve + const cost = COST[actor.location + rmap[i]] + 1; + const aCost = actor.time + cost; + const pressure = (totalTime - aCost) * valves[rmap[i]].rate; + const result = state.valves ^ i; + if (aCost > totalTime) continue; + // Push to open states + const newPressure = state.pressure + ((totalTime-aCost) * valves[rmap[i]].rate); + const newActors = [...state.actors]; + newActors[idx] = { + location: rmap[i], + time: aCost, + }; + + const cacheKey = result.toString(2); + if ((!map[cacheKey]) || (newPressure > map[cacheKey])) { + map[cacheKey] = newPressure; + openStates.push({ + valves: result, + actors: newActors, + pressure: newPressure, + }); + } + } + } + + } + + console.log(''); + console.log({ maxPressure }); + }); diff --git a/2022/day-16/sample b/2022/day-16/sample @@ -0,0 +1,10 @@ +Valve AA has flow rate=0; tunnels lead to valves DD, II, BB +Valve BB has flow rate=13; tunnels lead to valves CC, AA +Valve CC has flow rate=2; tunnels lead to valves DD, BB +Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE +Valve EE has flow rate=3; tunnels lead to valves FF, DD +Valve FF has flow rate=0; tunnels lead to valves EE, GG +Valve GG has flow rate=0; tunnels lead to valves FF, HH +Valve HH has flow rate=22; tunnel leads to valve GG +Valve II has flow rate=0; tunnels lead to valves AA, JJ +Valve JJ has flow rate=21; tunnel leads to valve II