diff --git a/2022/day16/input b/2022/day16/input new file mode 100644 index 0000000..c4760e8 --- /dev/null +++ b/2022/day16/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/2022/day16/main.go b/2022/day16/main.go index 155a802..862d421 100644 --- a/2022/day16/main.go +++ b/2022/day16/main.go @@ -2,111 +2,184 @@ package main import ( "fmt" - "math" + "regexp" + "strconv" "strings" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) +var valveregexp = regexp.MustCompile(`([A-Z]{2}).*=(\d+);.*?((?:[A-Z]{2}(?:, )?)+)`) + func main() { - inp := h.StdinToStringSlice() - part1(inp) + rooms := parseRooms(h.StdinToStringSlice()) + graph := floydWarshall(rooms) + + v := NewVolcano(rooms, graph) + r := part1(v) + v = NewVolcano(rooms, graph) + part2(v, r) } -func part1(inp []string) { - volcano := NewVolcano(inp) - timeLeft := 30 - currRoom := volcano.FindRoom("AA") - _, _ = timeLeft, currRoom - fmt.Println("Steps from AA -> JJ:", volcano.CountSteps("AA", "JJ")) +func part1(v *Volcano) uint16 { + var dfs func(target, pressure, minute, on, node uint16) uint16 + dfs = func(target, pressure, minute, on, node uint16) uint16 { + max := pressure + for _, w := range v.goodbits { + if node == w[0] || w[0] == v.start || w[0]&on != 0 { + continue + } + l := v.bitgraphs[node|w[0]] + 1 + if minute+l > target { + continue + } + if next := dfs(target, pressure+(target-minute-l)*w[1], minute+l, on|w[0], w[0]); next > max { + max = next + } + } + return max + } + + res := dfs(30, 0, 0, 0, v.start) + fmt.Println("# Part 1") + fmt.Println(res) + return res +} + +func part2(v *Volcano, p1 uint16) { + var dfspaths func(target, pressure, minute, on, node, path uint16) [][2]uint16 + dfspaths = func(target, pressure, minute, on, node, path uint16) [][2]uint16 { + paths := [][2]uint16{{pressure, path}} + for _, w := range v.goodbits { + if w[0] == node || w[0] == v.start || w[0]&on != 0 { + continue + } + l := v.bitgraphs[node|w[0]] + 1 + if minute+l > target { + continue + } + paths = append(paths, dfspaths(target, pressure+(target-minute-l)*w[1], minute+l, on|w[0], w[0], path|w[0])...) + } + return paths + } + + allpaths := dfspaths(26, 0, 0, 0, v.start, 0) + + // reduce paths (presumably, both paths are at least half of part 1) + var trimpaths [][2]uint16 + for _, p := range allpaths { + if p[0] > p1/2 { + trimpaths = append(trimpaths, p) + } + } + + // compare all paths to find max + var max uint16 = 0 + for idx := 0; idx < len(trimpaths); idx += 1 { + for jdx := idx + 1; jdx < len(trimpaths); jdx += 1 { + if trimpaths[idx][1]&trimpaths[jdx][1] != 0 { + continue + } + if m := trimpaths[idx][0] + trimpaths[jdx][0]; m > max { + max = m + } + } + } + + fmt.Println("# Part 2") + fmt.Println(max) } type Volcano struct { - rooms []*Room + start uint16 + + goodrooms []*Room + goodbits [][2]uint16 + + bitfield map[*Room]uint16 + bitgraphs []uint16 } -func NewVolcano(inp []string) *Volcano { - v := &Volcano{} - for i := range inp { - v.rooms = append(v.rooms, StringToRoom(inp[i])) - } - for i := range v.rooms { - for _, valve := range v.rooms[i].RawTunnels { - v.rooms[i].Tunnels = append(v.rooms[i].Tunnels, v.FindRoom(valve)) +func NewVolcano(rooms []*Room, graph map[*Room]map[*Room]uint16) *Volcano { + v := Volcano{} + + // pick valves with flow and starting point + for _, r := range rooms { + if r.Rate > 0 || r.Name == "AA" { + v.goodrooms = append(v.goodrooms, r) } } - return v -} -func (v *Volcano) CountSteps(from string, to string) int { - start, end := v.FindRoom(from), v.FindRoom(to) - if start == nil || end == nil { - fmt.Println("Couldn't find requested rooms:", from, ":", start, " ; ", to, ":", end) - return math.MaxInt + // assign bits + v.bitfield = make(map[*Room]uint16) + for idx, r := range v.goodrooms { + v.bitfield[r] = 1 << idx } - return v.TrackStepCount(start, end, []*Room{}) -} -func (v *Volcano) TrackStepCount(from *Room, to *Room, visited []*Room) int { - if from == to { - return 0 - } - minSteps := math.MaxInt - for _, t := range from.Tunnels { - fmt.Println("TrackStepCount:", from, "->", to, "::", t) - if !IsRoomIn(t, visited) { - fmt.Println(" Haven't Visited") - wrk := v.TrackStepCount(t, to, append(visited, t)) + 1 - fmt.Println(" From", from, "to", to, "in", wrk) - minSteps = h.Min(minSteps, wrk) + // find start + for _, r := range v.goodrooms { + if r.Name == "AA" { + v.start = v.bitfield[r] + break } } - return minSteps -} -func (v *Volcano) FindRoom(valve string) *Room { - for _, r := range v.rooms { - if r.Valve == valve { - return r + // create slice for fast edge lookup + v.bitgraphs = make([]uint16, 0xffff) + for _, v1 := range v.goodrooms { + for _, v2 := range v.goodrooms { + v.bitgraphs[v.bitfield[v1]|v.bitfield[v2]] = graph[v1][v2] } } - return nil -} -func IsRoomIn(room *Room, rooms []*Room) bool { - for i := range rooms { - if rooms[i] == room { - return true - } + // create slice for fast node lookup + v.goodbits = make([][2]uint16, len(v.goodrooms)) + for idx, r := range v.goodrooms { + v.goodbits[idx] = [2]uint16{v.bitfield[r], r.Rate} } - return false + return &v } type Room struct { - Valve string - FlowRate int - RawTunnels []string - Tunnels []*Room + Name string + Rate uint16 + Edges string } -func StringToRoom(s string) *Room { - room := &Room{} - r := strings.NewReader(s) - fmt.Fscanf(r, "Valve %s has flow rate=%d; tunnels lead to valve", &room.Valve, &room.FlowRate) - readTunnels := strings.Split(s, " valve")[1] - if readTunnels[0] == ' ' { - readTunnels = readTunnels[1:] - } else { - readTunnels = readTunnels[2:] +func parseRooms(inp []string) []*Room { + rooms := make([]*Room, len(inp)) + for idx, line := range inp { + m := valveregexp.FindStringSubmatch(line) + i, _ := strconv.Atoi(m[2]) + rooms[idx] = &Room{Name: m[1], Rate: uint16(i), Edges: m[3]} } - room.RawTunnels = strings.Split(readTunnels, ", ") - return room + return rooms } -func (r Room) String() string { - return r.Valve -} +func floydWarshall(valves []*Room) map[*Room]map[*Room]uint16 { + graph := make(map[*Room]map[*Room]uint16) + for _, v1 := range valves { + graph[v1] = make(map[*Room]uint16) + for _, v2 := range valves { + if v1 == v2 { + graph[v1][v2] = 0 + } else if strings.Contains(v1.Edges, v2.Name) { + graph[v1][v2] = 1 + } else { + graph[v1][v2] = 0xff + } + } + } -func (r Room) FullString() string { - return fmt.Sprintf("Valve %s has flow rate=%d; tunnels lead to valves %v", r.Valve, r.FlowRate, r.RawTunnels) + for _, k := range valves { + for _, i := range valves { + for _, j := range valves { + if graph[i][j] > graph[i][k]+graph[k][j] { + graph[i][j] = graph[i][k] + graph[k][j] + } + } + } + } + + return graph } diff --git a/2022/day16/problem b/2022/day16/problem index 7e726fe..4f6b025 100644 --- a/2022/day16/problem +++ b/2022/day16/problem @@ -1,46 +1,24 @@ - Advent of Code - - • [About] - • [Events] - • [Shop] - • [Settings] - • [Log Out] - - br0xen (AoC++) 29* - -       /^2022$/ - - • [Calendar] - • [AoC++] - • [Sponsors] - • [Leaderboard] - • [Stats] - - Our sponsors help make Advent of Code possible: - Sentry.io - More than 3.5 million developers across 85,000 organizations ship better software, faster, with Sentry. What - are you waiting for? +Advent of Code +br0xen (AoC++) 47* --- Day 16: Proboscidea Volcanium --- - The sensors have led you to the origin of the distress signal: yet another handheld device, just like the one the Elves - gave you. However, you don't see any Elves around; instead, the device is surrounded by elephants! They must have gotten - lost in these tunnels, and one of the elephants apparently figured out how to turn on the distress signal. + The sensors have led you to the origin of the distress signal: yet another handheld device, just like the one the Elves gave you. However, you don't see + any Elves around; instead, the device is surrounded by elephants! They must have gotten lost in these tunnels, and one of the elephants apparently + figured out how to turn on the distress signal. - The ground rumbles again, much stronger this time. What kind of cave is this, exactly? You scan the cave with your - handheld device; it reports mostly igneous rock, some ash, pockets of pressurized gas, magma... this isn't just a cave, - it's a volcano! + The ground rumbles again, much stronger this time. What kind of cave is this, exactly? You scan the cave with your handheld device; it reports mostly + igneous rock, some ash, pockets of pressurized gas, magma... this isn't just a cave, it's a volcano! - You need to get the elephants out of here, quickly. Your device estimates that you have 30 minutes before the volcano - erupts, so you don't have time to go back out the way you came in. + You need to get the elephants out of here, quickly. Your device estimates that you have 30 minutes before the volcano erupts, so you don't have time to + go back out the way you came in. - You scan the cave for other options and discover a network of pipes and pressure-release valves. You aren't sure how - such a system got into a volcano, but you don't have time to complain; your device produces a report (your puzzle input) - of each valve's flow rate if it were opened (in pressure per minute) and the tunnels you could use to move between the - valves. + You scan the cave for other options and discover a network of pipes and pressure-release valves. You aren't sure how such a system got into a volcano, + but you don't have time to complain; your device produces a report (your puzzle input) of each valve's flow rate if it were opened (in pressure per + minute) and the tunnels you could use to move between the valves. - There's even a valve in the room you and the elephants are currently standing in labeled AA. You estimate it will take - you one minute to open a single valve and one minute to follow any tunnel from one valve to another. What is the most - pressure you could release? + There's even a valve in the room you and the elephants are currently standing in labeled AA. You estimate it will take you one minute to open a single + valve and one minute to follow any tunnel from one valve to another. What is the most pressure you could release? For example, suppose you had the following scan output: @@ -55,16 +33,14 @@ Valve II has flow rate=0; tunnels lead to valves AA, JJ Valve JJ has flow rate=21; tunnel leads to valve II - All of the valves begin closed. You start at valve AA, but it must be damaged or jammed or something: its flow rate is - 0, so there's no point in opening it. However, you could spend one minute moving to valve BB and another minute opening - it; doing so would release pressure during the remaining 28 minutes at a flow rate of 13, a total eventual pressure - release of 28 * 13 = 364. Then, you could spend your third minute moving to valve CC and your fourth minute opening it, - providing an additional 26 minutes of eventual pressure release at a flow rate of 2, or 52 total pressure released by - valve CC. + All of the valves begin closed. You start at valve AA, but it must be damaged or jammed or something: its flow rate is 0, so there's no point in opening + it. However, you could spend one minute moving to valve BB and another minute opening it; doing so would release pressure during the remaining 28 + minutes at a flow rate of 13, a total eventual pressure release of 28 * 13 = 364. Then, you could spend your third minute moving to valve CC and your + fourth minute opening it, providing an additional 26 minutes of eventual pressure release at a flow rate of 2, or 52 total pressure released by valve + CC. - Making your way through the tunnels like this, you could probably open many or all of the valves by the time 30 minutes - have elapsed. However, you need to release as much pressure as possible, so you'll need to be methodical. Instead, - consider this approach: + Making your way through the tunnels like this, you could probably open many or all of the valves by the time 30 minutes have elapsed. However, you need + to release as much pressure as possible, so you'll need to be methodical. Instead, consider this approach: == Minute 1 == No valves are open. @@ -184,11 +160,93 @@ Work out the steps to release the most pressure in 30 minutes. What is the most pressure you can release? - To begin, get your puzzle input. + Your puzzle answer was 1862. - Answer: _____________________ [ [Submit] ] +--- Part Two --- - You can also [Shareon Twitter Mastodon] this puzzle. + You're worried that even with an optimal approach, the pressure released won't be enough. What if you got one of the elephants to help you? + + It would take you 4 minutes to teach an elephant how to open the right valves in the right order, leaving you with only 26 minutes to actually execute + your plan. Would having two of you working together be better, even if it means having less time? (Assume that you teach the elephant before opening any + valves yourself, giving you both the same full 26 minutes.) + + In the example above, you could teach the elephant to help you as follows: + + == Minute 1 == + No valves are open. + You move to valve II. + The elephant moves to valve DD. + + == Minute 2 == + No valves are open. + You move to valve JJ. + The elephant opens valve DD. + + == Minute 3 == + Valve DD is open, releasing 20 pressure. + You open valve JJ. + The elephant moves to valve EE. + + == Minute 4 == + Valves DD and JJ are open, releasing 41 pressure. + You move to valve II. + The elephant moves to valve FF. + + == Minute 5 == + Valves DD and JJ are open, releasing 41 pressure. + You move to valve AA. + The elephant moves to valve GG. + + == Minute 6 == + Valves DD and JJ are open, releasing 41 pressure. + You move to valve BB. + The elephant moves to valve HH. + + == Minute 7 == + Valves DD and JJ are open, releasing 41 pressure. + You open valve BB. + The elephant opens valve HH. + + == Minute 8 == + Valves BB, DD, HH, and JJ are open, releasing 76 pressure. + You move to valve CC. + The elephant moves to valve GG. + + == Minute 9 == + Valves BB, DD, HH, and JJ are open, releasing 76 pressure. + You open valve CC. + The elephant moves to valve FF. + + == Minute 10 == + Valves BB, CC, DD, HH, and JJ are open, releasing 78 pressure. + The elephant moves to valve EE. + + == Minute 11 == + Valves BB, CC, DD, HH, and JJ are open, releasing 78 pressure. + The elephant opens valve EE. + + (At this point, all valves are open.) + + == Minute 12 == + Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + + ... + + == Minute 20 == + Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + + ... + + == Minute 26 == + Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + + With the elephant helping, after 26 minutes, the best you could do would release a total of 1707 pressure. + + With you and an elephant working together for 26 minutes, what is the most pressure you could release? + + Your puzzle answer was 2422. + + Both parts of this puzzle are complete! They provide two gold stars: ** References @@ -196,7 +254,6 @@ References . https://adventofcode.com/ . https://adventofcode.com/2022/about . https://adventofcode.com/2022/events - . https://teespring.com/stores/advent-of-code . https://adventofcode.com/2022/settings . https://adventofcode.com/2022/auth/logout . Advent of Code Supporter @@ -208,7 +265,5 @@ References . https://adventofcode.com/2022/leaderboard . https://adventofcode.com/2022/stats . https://adventofcode.com/2022/sponsors - . https://sentry.io/ + . https://adventofcode.com/2022 . https://adventofcode.com/2022/day/16/input - . https://twitter.com/intent/tweet?text=%22Proboscidea+Volcanium%22+%2D+Day+16+%2D+Advent+of+Code+2022&url=https%3A%2F%2Fadventofcode%2Ecom%2F2022%2Fday%2F16&related=ericwastl&hashtags=AdventOfCode - . javascript:void(0);