162 lines
4.7 KiB
Go
162 lines
4.7 KiB
Go
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
|
|
}
|