249 lines
5.6 KiB
Go
249 lines
5.6 KiB
Go
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
|
|
}
|
|
|