Advent of Code 2022 - Day 16: Proboscidea Volcanium Solution
Floyd-Warshal and bitmaskPart 1
Use Floyd-Warshal algorithm to calculate minimum travel time among tunnels
Ignore valves with flow rate of 0 during calculation
Use bitmask to represent openned valves
Use DFS to travese and calculate best flow rate through all possibilites
Implementation
1const fs = require("fs")23const readData = () => {4 const data = fs5 .readFileSync("./input", "utf-8")6 .split(/\r?\n/)7 .map(line =>8 /Valve (\w+) has flow rate=(\d+); tunnels? leads? to valves? (.+)/g.exec(9 line10 )11 )12 .map(([_, valve, rate, to]) => ({13 valve,14 rate: Number(rate),15 to: to.split(", "),16 }))17 .reduce(18 (acc, { valve, rate, to }) =>19 Object.assign(acc, { [valve]: { rate, to } }),20 {}21 )2223 return data24}2526const main = () => {27 const data = readData()2829 const valves = Object.keys(data)3031 const valvesHavingPositiveRate = valves.filter(valve =>32 Boolean(data[valve].rate)33 )3435 const valveBitRepr = valvesHavingPositiveRate.reduce(36 (acc, valve, i) => Object.assign(acc, { [valve]: 1 << i }),37 {}38 )3940 const minTime = valves.reduce((acc, valveFrom) => {41 acc[valveFrom] = valves.reduce((acc, valveTo) => {42 acc[valveTo] = data[valveFrom].to.includes(valveTo)43 ? 144 : Number.POSITIVE_INFINITY45 return acc46 }, {})47 return acc48 }, {})4950 for (const mid of valves) {51 for (const start of valves) {52 for (const end of valves) {53 minTime[start][end] = Math.min(54 minTime[start][end],55 minTime[start][mid] + minTime[mid][end]56 )57 }58 }59 }6061 const isVisited = (bitReprA, bitReprB) => bitReprA & bitReprB6263 const recursion = ({64 minutesLeft,65 currentValve,66 openedBitRepr,67 rate,68 bitReprMaxRateMap,69 }) => {70 bitReprMaxRateMap[openedBitRepr] = Math.max(71 bitReprMaxRateMap[openedBitRepr] || 0,72 rate73 )7475 for (const valve of valvesHavingPositiveRate) {76 const newMinutesLeft = minutesLeft - (minTime[currentValve][valve] + 1)77 const newRate = rate + newMinutesLeft * data[valve].rate78 const newOpenedBitRepr = openedBitRepr | valveBitRepr[valve]7980 if (newMinutesLeft <= 0 || isVisited(valveBitRepr[valve], openedBitRepr))81 continue8283 recursion({84 minutesLeft: newMinutesLeft,85 currentValve: valve,86 openedBitRepr: newOpenedBitRepr,87 rate: newRate,88 bitReprMaxRateMap,89 })90 }9192 return bitReprMaxRateMap93 }9495 const bitReprMaxRateMap = recursion({96 minutesLeft: 30,97 currentValve: "AA",98 openedBitRepr: 0,99 rate: 0,100 bitReprMaxRateMap: {},101 })102103 const res = Math.max(...Object.values(bitReprMaxRateMap))104105 console.log(res)106}107108main()
Part 2
Like part 1, human go first and elephant avoid opened valves
1humanOpened & elephantOpened == 0
Implementation
1const fs = require("fs")23const readData = () => {4 const data = fs5 .readFileSync("./input", "utf-8")6 .split(/\r?\n/)7 .map(line =>8 /Valve (\w+) has flow rate=(\d+); tunnels? leads? to valves? (.+)/g.exec(9 line10 )11 )12 .map(([_, valve, rate, to]) => ({13 valve,14 rate: Number(rate),15 to: to.split(", "),16 }))17 .reduce(18 (acc, { valve, rate, to }) =>19 Object.assign(acc, { [valve]: { rate, to } }),20 {}21 )2223 return data24}2526const main = () => {27 const data = readData()2829 const valves = Object.keys(data)3031 const valvesHavingPositiveRate = valves.filter(valve =>32 Boolean(data[valve].rate)33 )3435 const valveBitRepr = valvesHavingPositiveRate.reduce(36 (acc, valve, i) => Object.assign(acc, { [valve]: 1 << i }),37 {}38 )3940 const minTime = valves.reduce((acc, valveFrom) => {41 acc[valveFrom] = valves.reduce((acc, valveTo) => {42 acc[valveTo] = data[valveFrom].to.includes(valveTo)43 ? 144 : Number.POSITIVE_INFINITY45 return acc46 }, {})47 return acc48 }, {})4950 for (const mid of valves) {51 for (const start of valves) {52 for (const end of valves) {53 minTime[start][end] = Math.min(54 minTime[start][end],55 minTime[start][mid] + minTime[mid][end]56 )57 }58 }59 }6061 const isVisited = (bitReprA, bitReprB) => bitReprA & bitReprB6263 const recursion = ({64 minutesLeft,65 currentValve,66 openedBitRepr,67 rate,68 bitReprMaxRateMap,69 }) => {70 bitReprMaxRateMap[openedBitRepr] = Math.max(71 bitReprMaxRateMap[openedBitRepr] || 0,72 rate73 )7475 for (const valve of valvesHavingPositiveRate) {76 const newMinutesLeft = minutesLeft - (minTime[currentValve][valve] + 1)77 const newRate = rate + newMinutesLeft * data[valve].rate78 const newOpenedBitRepr = openedBitRepr | valveBitRepr[valve]7980 if (newMinutesLeft <= 0 || isVisited(valveBitRepr[valve], openedBitRepr))81 continue8283 recursion({84 minutesLeft: newMinutesLeft,85 currentValve: valve,86 openedBitRepr: newOpenedBitRepr,87 rate: newRate,88 bitReprMaxRateMap,89 })90 }9192 return bitReprMaxRateMap93 }9495 const bitReprMaxRateMap = recursion({96 minutesLeft: 26,97 currentValve: "AA",98 openedBitRepr: 0,99 rate: 0,100 bitReprMaxRateMap: {},101 })102103 let res = Number.NEGATIVE_INFINITY104105 for (const human of Object.entries(bitReprMaxRateMap)) {106 for (const elephant of Object.entries(bitReprMaxRateMap)) {107 if (!(human[0] & elephant[0])) {108 res = Math.max(res, human[1] + elephant[1])109 }110 }111 }112113 console.log(res)114}115116main()
References
Comments
Loading comments...
Tags
adventofcode
recursion
dfs
dynamic programming
bit manipulation
Apply and earn a $2,500 bonus once you're hired on your first job!
Clients from the Fortune 500 to Silicon Valley startups
Choose your own rate, get paid on time
From hourly, part-time, to full-time positions
Flexible remote working environment
A lot of open JavaScript jobs!!
Fact corner: Referred talent are 5x more likely to pass the Toptal screening process than the average applicant.
Still hesitate? Read HoningJS author's guide on dealing with Toptal interview process.