2022 Day 16 Complete
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user