From d0e924934e22ce982aec27bd5ff736ff87ba6b46 Mon Sep 17 00:00:00 2001 From: Ben Ashton Date: Mon, 19 Dec 2022 17:13:00 -0700 Subject: [PATCH] Day 16 --- 16/input | 52 +++++++++++++++++ 16/solution.mjs | 152 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 16/input create mode 100644 16/solution.mjs diff --git a/16/input b/16/input new file mode 100644 index 0000000..c4760e8 --- /dev/null +++ b/16/input @@ -0,0 +1,52 @@ +Valve AP has flow rate=0; tunnels lead to valves AA, ON +Valve QN has flow rate=21; tunnels lead to valves RI, CG +Valve LK has flow rate=0; tunnels lead to valves XM, AA +Valve HA has flow rate=0; tunnels lead to valves WH, KF +Valve DS has flow rate=16; tunnel leads to valve II +Valve KD has flow rate=0; tunnels lead to valves KG, QB +Valve JW has flow rate=0; tunnels lead to valves AD, KF +Valve HU has flow rate=0; tunnels lead to valves UK, CO +Valve AE has flow rate=10; tunnels lead to valves IR, PT, UV +Valve XA has flow rate=0; tunnels lead to valves CG, EU +Valve SE has flow rate=17; tunnels lead to valves YR, AD +Valve TR has flow rate=0; tunnels lead to valves AL, CS +Valve BS has flow rate=0; tunnels lead to valves YH, XM +Valve IJ has flow rate=24; tunnels lead to valves XN, WE +Valve AA has flow rate=0; tunnels lead to valves LK, AP, IZ, PC, QD +Valve KG has flow rate=0; tunnels lead to valves KD, CS +Valve QV has flow rate=0; tunnels lead to valves XM, II +Valve PC has flow rate=0; tunnels lead to valves AA, YF +Valve GJ has flow rate=20; tunnel leads to valve RI +Valve UV has flow rate=0; tunnels lead to valves UK, AE +Valve IR has flow rate=0; tunnels lead to valves EU, AE +Valve EU has flow rate=13; tunnels lead to valves IR, DT, XA, ON +Valve ED has flow rate=0; tunnels lead to valves XN, CO +Valve DT has flow rate=0; tunnels lead to valves EU, UK +Valve YE has flow rate=0; tunnels lead to valves XM, WS +Valve AD has flow rate=0; tunnels lead to valves JW, SE +Valve WE has flow rate=0; tunnels lead to valves IJ, NA +Valve UK has flow rate=5; tunnels lead to valves UV, DT, QD, HU +Valve YR has flow rate=0; tunnels lead to valves OS, SE +Valve II has flow rate=0; tunnels lead to valves QV, DS +Valve GT has flow rate=0; tunnels lead to valves CS, MN +Valve YH has flow rate=0; tunnels lead to valves BS, QB +Valve BQ has flow rate=0; tunnels lead to valves XM, KF +Valve OS has flow rate=0; tunnels lead to valves YR, NA +Valve WH has flow rate=0; tunnels lead to valves QB, HA +Valve QB has flow rate=4; tunnels lead to valves WH, KD, YH, IZ +Valve ON has flow rate=0; tunnels lead to valves AP, EU +Valve IZ has flow rate=0; tunnels lead to valves AA, QB +Valve MN has flow rate=25; tunnel leads to valve GT +Valve CG has flow rate=0; tunnels lead to valves XA, QN +Valve QD has flow rate=0; tunnels lead to valves UK, AA +Valve AL has flow rate=0; tunnels lead to valves KF, TR +Valve XN has flow rate=0; tunnels lead to valves ED, IJ +Valve WS has flow rate=0; tunnels lead to valves YE, CS +Valve CO has flow rate=18; tunnels lead to valves ED, PT, HU +Valve PT has flow rate=0; tunnels lead to valves CO, AE +Valve RI has flow rate=0; tunnels lead to valves QN, GJ +Valve CS has flow rate=9; tunnels lead to valves YF, GT, WS, TR, KG +Valve YF has flow rate=0; tunnels lead to valves PC, CS +Valve NA has flow rate=23; tunnels lead to valves OS, WE +Valve KF has flow rate=12; tunnels lead to valves HA, AL, JW, BQ +Valve XM has flow rate=3; tunnels lead to valves LK, QV, YE, BS, BQ diff --git a/16/solution.mjs b/16/solution.mjs new file mode 100644 index 0000000..b67e111 --- /dev/null +++ b/16/solution.mjs @@ -0,0 +1,152 @@ + +import { readFileSync } from 'node:fs'; + +const input = readFileSync('input', 'utf-8'); + + +const valves = new Map(input + .split('\n') + .filter(Boolean) + .map(line => { + const valve = line.match(/Valve (\w+)/)[1]; + const rate = parseInt(line.match(/rate=(\d+)/)[1]); + const to = line.match(/valves? (.*)/)[1].split(', '); + return [valve, {valve, rate, to}]; + }) +); + +function shortestPath(v, vDest, path=[]) { + const valve = valves.get(v); + + if (valve.to.includes(vDest)) { + return [...path, v, vDest]; + } + return valve.to + .filter(to => !path.includes(to)) + .map(to => shortestPath(to, vDest, [...path, v])) + .filter(Boolean) + .sort((p1, p2) => p1.length - p2.length)[0]; +} + +// Add a property to all valves containing links to all other valves with a +// flow rate greater than 0 and the time it will take to reach them +const valvesWithFlow = [...valves.values()] + .filter(valve => valve.rate > 0); +for (const valve of valves.values()) { + valve.usefulTo = valvesWithFlow + .filter(vDest => vDest.valve !== valve.valve) + .map(vDest => ({ + valve: vDest.valve, + time: shortestPath(valve.valve, vDest.valve).length - 1, + rate: vDest.rate + })); +} + +function product(...arrs) { + if (!arrs.length) return []; + return arrs.reduce((result, arr) => + result.flatMap(r => + arr.map(a => [...r, a]) + ), + [[]] + ) +} + +// General solution for n players +function solve(players, rate=0) { + for (const player of players) { + const valve = valves.get(player.path[player.path.length - 1]); + + if (valve.rate > 0 && !player.opened) { + player.opened = true; + player.timeRemaining -= 1; + rate += valve.rate * Math.max(0, player.timeRemaining); + } + } + + // Array of valve names which haven't been visited + const unvisitedValves = valvesWithFlow + .map(v => v.valve) + .filter(valve => !players.find(p => p.path.includes(valve))); + + // Exit conditions + if ( + unvisitedValves.length === 0 || + players.every(player => player.timeRemaining <= 0) + ) { + return [rate, players]; + } + + // Decide who is moving + const maxTimeRemaining = players.reduce( + (max, player) => Math.max(max, player.timeRemaining), + 0 + ); + const movingPlayers = players.filter( + player => player.timeRemaining === maxTimeRemaining + ); + const nonMovingPlayers = players.filter( + player => !movingPlayers.includes(player) + ); + + const playerMoves = product(...movingPlayers + .map(player => { + const valve = valves.get(player.path[player.path.length - 1]); + const destinations = valve.usefulTo + .filter(to => unvisitedValves.includes(to.valve)) + .filter((to, _, tos) => !tos.find((otherTo) => + otherTo.rate > to.rate && + otherTo.time <= to.time + )); + if (destinations.length < movingPlayers.length) { + destinations.push(undefined); + } + return destinations; + }) + ).filter(moves => { + const valves = moves.filter(Boolean).map(to => to.valve); + return ( + valves.length !== 0 && + new Set(valves).size === valves.length + ); + }); + + return playerMoves + .map(moves => { + const movedPlayers = moves.map((to, i) => { + const player = movingPlayers[i]; + if (to === undefined) { + // Not moving + return player; + } + return { + timeRemaining: player.timeRemaining - to.time, + opened: false, + path: [...player.path, to.valve] + }; + }); + + return solve([...nonMovingPlayers, ...movedPlayers], rate); + }) + .filter(Boolean) + .sort(([rate1], [rate2]) => rate2 - rate1)[0]; +} + + +const solution1 = solve([{ + timeRemaining: 30, + opened: false, + path: ['AA'] +}]); +console.log(`Max pressure released in 30 minutes: ${solution1[0]}`); + +const solution2 = solve([{ + timeRemaining: 26, + opened: false, + path: ['AA'] +}, { + timeRemaining: 26, + opened: false, + path: ['AA'] +}]); +console.log(`Max pressure released with elephant: ${solution2[0]}`);