package day23 import ( "fmt" "log" "slices" mapset "github.com/deckarep/golang-set/v2" ) type Node struct { index int c Coord name string } func (n Node)Name() string { var r string if n.index < 25 { num := 'A' + n.index r = string(rune(num)) } else { num := 'a' + n.index - 25 r = string(rune(num)) } return r } type Graph struct { nodes map[Coord]Node nodesByIndex []Node edges [][]int // from, to, length. excluding from, including to } func MaxDist(from, to Node) (result int) { return } func PrintFieldWithGraph(g Graph, f Field) (result string) { result += "\n" for row := 0; row <= f.MaxRow; row++ { for col := 0; col <= f.MaxCol; col++ { symb := f.Cells[Coord{Row: row, Col: col}] if symb != Tree { coord := Coord{Row: row, Col: col} node, exists := g.nodes[coord] if exists { result += fmt.Sprint(node.Name()) } else { result += "." } } else { result += " " } } result += "\n" } return } func CreateGraph(f Field) (g Graph) { startCoord := Coord{Row: 0, Col: f.StartCol} // directly below start initialPath := PathEnd{ end: Coord{Row: 1, Col: f.StartCol}, visited: mapset.NewSet[Coord](), } g = Graph{ nodes: map[Coord]Node{ startCoord: Node{ index: 0, c: startCoord, name: "A", }, }, } const presumedNodeCount = 36 g.edges = make([][]int, presumedNodeCount) for i := 0; i < presumedNodeCount; i++ { g.edges[i] = make([]int, presumedNodeCount) } recursiveGraphStep(f, initialPath, &g, startCoord, 1, mapset.NewSet[Coord]()) g.edges[0][0] = 0 g.nodesByIndex = make([]Node, len(g.nodes)) for _, node := range g.nodes { g.nodesByIndex[node.index] = node } return } func (g *Graph)Neighbors(node Node) (nodes []Node) { index := node.index for toIndex, len := range g.edges[index] { if len > 0 { nodes = append(nodes, g.nodesByIndex[toIndex]) } } return } var maxSoFar int = -1 func CheckMaxSoFar(maybe int) { if maybe > maxSoFar { maxSoFar = maybe } } func (g *Graph) DFSLenOnGraph(atNode Node, visited mapset.Set[int], toNode Node, lenSoFar int) int { if atNode == toNode { CheckMaxSoFar(lenSoFar) return lenSoFar } log.Printf("at %+v to %+v cur dist is %d.\t\t|max so far %d| \n", atNode, toNode, lenSoFar, maxSoFar) neighbors := g.Neighbors(atNode) toVisit := slices.DeleteFunc(neighbors, func(n Node) bool { return visited.Contains(n.index) }) if len(toVisit) == 0 { return -1 } max := -1 for _, nextNode := range toVisit { newVisited := visited.Clone() newVisited.Add(atNode.index) dist := g.edges[atNode.index][nextNode.index] maxFromNext := g.DFSLenOnGraph(nextNode, newVisited, toNode, lenSoFar + dist) if maxFromNext > max { max = maxFromNext } } return max } // run dfs, remembering from which remembers from which node we go, which path already traversed func recursiveGraphStep(f Field, p PathEnd, g *Graph, goingFrom Coord, goingLen int, visitedPathPoints mapset.Set[Coord]) { // log.Printf("entering coord %+v. from %+v with len %d\n", p.end, goingFrom, goingLen) // if visitedPathPoints.Contains(p.end) { // return // } neighbors := f.NeighborsPart2(p.end) isCrossRoad := len(neighbors) > 2 if isCrossRoad { log.Println("this should be crossroad ", p.end) } isStart := p.end == Coord{Row: 0, Col: f.StartCol} isEnd := p.end == f.EndCoord() if isEnd { log.Println("this should be end ", p.end) } isNode := isCrossRoad || isStart || isEnd continuedPaths := ExtendPath(p, f) if !isNode { // just recurse into next paths, from same node, with increased len visitedPathPoints.Add(p.end) for _, nextStep := range continuedPaths { recursiveGraphStep(f, nextStep, g, goingFrom, goingLen+1, visitedPathPoints) } } else { node, known := g.nodes[p.end] // check if known, if not known - create if !known { node = Node{ c: p.end, index: len(g.nodes), } node.name = node.Name() g.nodes[p.end] = node log.Printf("creating node %s %+v\n", node.Name(), node) } from := g.nodes[goingFrom] log.Printf("from %s to %s\n", from.Name(), node.Name()) // and add vertices to currently traversed if g.edges[node.index][from.index] == 0 { g.edges[node.index][from.index] = goingLen g.edges[from.index][node.index] = goingLen } else { knownEdge := g.edges[node.index][from.index] if goingLen > knownEdge { g.edges[node.index][from.index] = goingLen g.edges[from.index][node.index] = goingLen } } // NOTE ah, it's possible to have two edges between i and j // but, i only need the longest one // log.Printf("adding edges between %d & %d of len %d\n", node.index, from.index, goingLen) // continue with new 'from' and len of 1 if !known { for _, nextStep := range continuedPaths { log.Printf("from %s should recurse to %+v", node.Name(), nextStep) recursiveGraphStep(f, nextStep, g, p.end, 1, visitedPathPoints) } } } return } func GraphToMermaid(g Graph) (result string) { result += "\nflowchart LR\n" lines := mapset.NewSet[string]() for _, node := range g.nodes { for to, len := range g.edges[node.index] { var toNode Node for _, other := range g.nodes { if other.index == to { toNode = other } } if len > 0 { var fromName, toName string if node.index < toNode.index { fromName = node.Name() toName = toNode.Name() } else { fromName = toNode.Name() toName = node.Name() } line := fmt.Sprintf("\t%s---|length %d|%s\n", fromName, len, toName) lines.Add(line) } } // result += fmt.Sprintf("%s--|%d|%s\n", a ...any) } for line := range lines.Iter() { result += line } return }