package day23 import ( "fmt" "log" mapset "github.com/deckarep/golang-set/v2" ) type PathEnd struct { end Coord visited mapset.Set[Coord] } func (p PathEnd)Sring() string { return fmt.Sprintf("PathEnd[at %+v, visited: %+v]", p.end, p.visited) } func ExtendPath(p PathEnd, f Field) (nextPaths []PathEnd) { endPointNeighbors := f.NeighborsPart2(p.end) for _, potentialNewEnd := range endPointNeighbors { if !p.visited.Contains(potentialNewEnd) { nextVisited := p.visited.Clone() nextVisited.Add(p.end) nextPaths = append(nextPaths, PathEnd{ end: potentialNewEnd, visited: nextVisited, }) } } return } // info on path from start to end type PathInfo struct { Visited mapset.Set[Coord] } func RunDFSTingy(f Field) []PathInfo { initialPath := PathEnd{ end: Coord{Row: 0, Col: f.StartCol}, visited: mapset.NewSet[Coord](), } initialShared := make(map[Coord][]PathInfo) return DFSScenicPaths(f, initialPath, initialShared) } var knownMax int = 0 func CheckAndPrintMax(maybeNewMax int) { if maybeNewMax > knownMax { log.Printf("\n\n>>>>found new max: %d\n", maybeNewMax) knownMax = maybeNewMax } } func DFSScenicPaths(f Field, curPath PathEnd, sharedMem map[Coord][]PathInfo) (pathsFromTheStartToEnd []PathInfo) { curCoord := curPath.end if curCoord == (Coord{ Row: 6, Col: 15 }) { log.Println(">>>>>>>>") } // log.Printf("entering %+v with mem %+v\n", curPath, sharedMem[curCoord]) if curCoord == f.EndCoord() { pathsFromTheStartToEnd = append(pathsFromTheStartToEnd, PathInfo{curPath.visited.Clone()}) log.Printf("got to end. cur len is %d\n", curPath.visited.Cardinality()) CheckAndPrintMax(curPath.visited.Cardinality()) // i guess return only from current to end? // and on non terminal first time, return copy with self added? return } // now for non final point knownPaths, visitedBefore := sharedMem[curCoord] // NOTE but this only if we haven't visited this coord before! if !visitedBefore { nextSteps := ExtendPath(curPath, f) suffixesFromCurToEnd := make([]PathInfo, 0) for _, nextPath := range nextSteps { pathsToEndThrough := DFSScenicPaths(f, nextPath, sharedMem) // i guess here deduct the prefix. for _, path := range pathsToEndThrough { // will contain this and further suffix := PathInfo{ Visited: path.Visited.Difference(curPath.visited).Clone(), } // log.Printf(">> from path \n%+v make suffix \n%+v\n\n", path, suffix) suffixesFromCurToEnd = append(suffixesFromCurToEnd, suffix) } pathsFromTheStartToEnd = append(pathsFromTheStartToEnd, pathsToEndThrough...) if len(pathsToEndThrough) != 0 { // log.Printf("setting mem for %+v to %+v", curCoord, suffixesFromCurToEnd) sharedMem[curCoord] = suffixesFromCurToEnd } } return } else { // have visited this point before, due to dfs all possible paths to end should already be known // so curPath.visited should contian a detour. // need to figure out if this detour is compatible with any of the known paths to end // from those create 'new' paths to end with that detour // return those and add those to the shared mem for _, knownPathToEnd := range knownPaths { // those are all points through which this known path goes from current to end // if our curPath fromCurToEnd := knownPathToEnd.Visited thisPrefix := curPath.visited if thisPrefix.Intersect(fromCurToEnd).Cardinality() == 0 { // then current prefix is compatible with this path. fromCurPrefixToEnd := thisPrefix.Clone() fromCurPrefixToEnd.Union(fromCurToEnd) pathsFromTheStartToEnd = append(pathsFromTheStartToEnd, PathInfo{fromCurPrefixToEnd}) log.Printf("additional path to end of len %d\n", fromCurPrefixToEnd.Cardinality()) CheckAndPrintMax(fromCurPrefixToEnd.Cardinality()) } } log.Printf("having second visit into %+v.\n", curPath) return } panic("should not be reachable") } // return paths that end on End func RunAllScenicPaths(f Field) (result []PathEnd) { pathsToFurther := []PathEnd{ {end: Coord{Row: 0, Col: f.StartCol}, visited: mapset.NewSet[Coord]()}, } for len(pathsToFurther) > 0 { curCheckedPath := pathsToFurther[0] pathsToFurther = pathsToFurther[1:] if curCheckedPath.end == f.EndCoord() { result = append(result, curCheckedPath) // log.Printf("found end path of len %d . %+v", curCheckedPath.visited.Cardinality(), curCheckedPath) continue } nextSteps := ExtendPath(curCheckedPath, f) // log.Printf("for %+v next steps %+v\n", curCheckedPath, pathsToFurther) // log.Printf("remaining paths to check len is %d", len(pathsToFurther)) // log.Println(pathsToFurther) if len(nextSteps) > 0 { pathsToFurther = append(pathsToFurther, nextSteps...) } } return }