186 lines
4.0 KiB
Go
186 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
)
|
|
|
|
var valveregexp = regexp.MustCompile(`([A-Z]{2}).*=(\d+);.*?((?:[A-Z]{2}(?:, )?)+)`)
|
|
|
|
func main() {
|
|
rooms := parseRooms(h.StdinToStringSlice())
|
|
graph := floydWarshall(rooms)
|
|
|
|
v := NewVolcano(rooms, graph)
|
|
r := part1(v)
|
|
v = NewVolcano(rooms, graph)
|
|
part2(v, r)
|
|
}
|
|
|
|
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 {
|
|
start uint16
|
|
|
|
goodrooms []*Room
|
|
goodbits [][2]uint16
|
|
|
|
bitfield map[*Room]uint16
|
|
bitgraphs []uint16
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
// assign bits
|
|
v.bitfield = make(map[*Room]uint16)
|
|
for idx, r := range v.goodrooms {
|
|
v.bitfield[r] = 1 << idx
|
|
}
|
|
|
|
// find start
|
|
for _, r := range v.goodrooms {
|
|
if r.Name == "AA" {
|
|
v.start = v.bitfield[r]
|
|
break
|
|
}
|
|
}
|
|
|
|
// 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]
|
|
}
|
|
}
|
|
|
|
// 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 &v
|
|
}
|
|
|
|
type Room struct {
|
|
Name string
|
|
Rate uint16
|
|
Edges string
|
|
}
|
|
|
|
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]}
|
|
}
|
|
return rooms
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|