package day16 import ( "bufio" "fmt" "os" "strings" "unicode" ) type Valve struct { flowRate int leadsTo []string } type SimulationPoint struct { time int imAt string elephantAt string opened string } type MovePair struct { me string elephant string } func newMovePair(useElephant bool, a string, b string) MovePair { if useElephant && a != "**" && b != "**" && b < a { return MovePair{b, a} } else { return MovePair{a, b} } } func addMovePair(pair MovePair, list *[]MovePair) { for _, existing := range *list { if existing.me == pair.me && existing.elephant == pair.elephant { return } } *list = append(*list, pair) } func alreadyVisited(place string, point SimulationPoint) bool { for i := 0; i < len(point.opened); i += 2 { if place[0] == point.opened[i] && place[1] == point.opened[i+1] { return true } } return false } func addPlace(place string, opened string) string { injectionPoint := 0 for injectionPoint < len(opened) && (opened[injectionPoint] < place[0] || (opened[injectionPoint] == place[0] && opened[injectionPoint+1] < place[1])) { injectionPoint += 2 } return opened[0:injectionPoint] + place + opened[injectionPoint:] } var everythingOpened string var simulatedItems map[SimulationPoint]int func runSimulations(useElephant bool, time int, myPlace string, elephantAt string, opened string, valves map[string]Valve) int { maxTime := 30 if useElephant { maxTime = 26 } if time == maxTime || everythingOpened == opened { return 0 } point := SimulationPoint{time, myPlace, elephantAt, opened} if previousValue, ok := simulatedItems[point]; ok { return previousValue } bestResult := 0 myMoves := []string{} if !alreadyVisited(myPlace, point) && valves[myPlace].flowRate != 0 { myMoves = append(myMoves, "**") } myMoves = append(myMoves, valves[myPlace].leadsTo...) elephantMoves := []string{"AA"} if useElephant { elephantMoves = []string{} if !alreadyVisited(elephantAt, point) && valves[elephantAt].flowRate != 0 && myPlace != elephantAt { elephantMoves = append(elephantMoves, "**") } elephantMoves = append(elephantMoves, valves[elephantAt].leadsTo...) } moves := []MovePair{} for _, myMove := range myMoves { for _, elephantMove := range elephantMoves { pair := newMovePair(useElephant, myMove, elephantMove) addMovePair(pair, &moves) } } for _, pair := range moves { myMove := pair.me elephantMove := pair.elephant openReleases := 0 myNext := myPlace elephantNext := elephantAt nowOpen := opened if myMove == "**" { openReleases += valves[myPlace].flowRate * (maxTime - (time + 1)) nowOpen = addPlace(myPlace, nowOpen) } else { myNext = myMove } if elephantMove == "**" { openReleases += valves[elephantAt].flowRate * (maxTime - (time + 1)) nowOpen = addPlace(elephantAt, nowOpen) } else { elephantNext = elephantMove } candidate := openReleases + runSimulations(useElephant, time+1, myNext, elephantNext, nowOpen, valves) if candidate > bestResult { bestResult = candidate } } simulatedItems[point] = bestResult return bestResult } func Run(filename string) { file, err := os.Open(filename) if err != nil { fmt.Println("Error opening file:", err) return } scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) valveMap := map[string]Valve{} everythingOpened = "" for scanner.Scan() { line := scanner.Text() var valve string var rate int fmt.Sscanf(line, "Valve %s has flow rate=%d", &valve, &rate) _, after_semi, found_semi := strings.Cut(line, ";") if !found_semi { fmt.Println("PANIC: Couldn't find semi colon in line.") return } after_semi = strings.TrimLeftFunc(after_semi, func(r rune) bool { return !unicode.IsUpper(r) }) targets := strings.Split(after_semi, ", ") if rate > 0 { everythingOpened = addPlace(valve, everythingOpened) } valveMap[valve] = Valve{rate, targets} } simulatedItems = map[SimulationPoint]int{} bestRelease1 := runSimulations(false, 0, "AA", "AA", "", valveMap) fmt.Println("Working on my own, best pressure release found is", bestRelease1) simulatedItems = map[SimulationPoint]int{} bestRelease2 := runSimulations(true, 0, "AA", "AA", "", valveMap) fmt.Println("Working with an elephant, best pressure release found is", bestRelease2) file.Close() }