2022 Day 16 Complete

This commit is contained in:
2022-12-28 13:37:03 -06:00
parent d940cade58
commit 562faef625
3 changed files with 305 additions and 125 deletions

View File

@@ -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
}