Ben Ashton
1 year ago
2 changed files with 204 additions and 0 deletions
@ -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 |
@ -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]}`); |
Loading…
Reference in new issue