diff --git a/day23/aLongWalk.go b/day23/aLongWalk.go index aa29eb2..07faf47 100644 --- a/day23/aLongWalk.go +++ b/day23/aLongWalk.go @@ -2,24 +2,25 @@ package day23 import ( "fmt" - "log" ) // length of longest scenic route func Run() int { fmt.Println("day 23") + max := 0 filename := "day23/input" field := ReadField(filename) - finalPaths := RunDFSTingy(field) - // log.Println(finalPaths) + fmt.Println(field.SparseString()) + // finalPaths := RunDFSTingy(field) + // // log.Println(finalPaths) + + // for _, path := range finalPaths { + // if path.Visited.Cardinality() > max { + // log.Println("one path len is ", path.Visited.Cardinality()) + // max = path.Visited.Cardinality() + // } + // } - max := 0 - for _, path := range finalPaths { - if path.Visited.Cardinality() > max { - log.Println("one path len is ", path.Visited.Cardinality()) - max = path.Visited.Cardinality() - } - } return max } diff --git a/day23/field.go b/day23/field.go index 8d841d7..aa1881f 100644 --- a/day23/field.go +++ b/day23/field.go @@ -118,6 +118,36 @@ func (f *Field) String() (result string) { return } +func (f *Field) SparseString() (result string) { + result += "\n" + for row := 0; row <= f.MaxRow; row++ { + for col := 0; col <= f.MaxCol; col++ { + if row == 0 && col == f.StartCol { + result += "S" + continue + } + if row == f.MaxRow && col == f.EndCol { + result += "E" + continue + } + + symb := f.Cells[Coord{Row: row, Col: col}] + if symb != Tree { + neighbors := f.NeighborsPart2(Coord{Row: row, Col: col}) + if len(neighbors) > 2 { + result += "o" + } else { + result += "." + } + } else { + result += " " + } + } + result += "\n" + } + return +} + func ReadField(filename string) (result Field) { bytes, err := os.ReadFile(filename) if err != nil { diff --git a/day23/graph.go b/day23/graph.go new file mode 100644 index 0000000..e22991d --- /dev/null +++ b/day23/graph.go @@ -0,0 +1,248 @@ +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 +} + diff --git a/day23/graph_test.go b/day23/graph_test.go new file mode 100644 index 0000000..926891a --- /dev/null +++ b/day23/graph_test.go @@ -0,0 +1,101 @@ +package day23 + +import ( + "fmt" + "os" + "testing" + + mapset "github.com/deckarep/golang-set/v2" +) + +func TestGraphCreate(t *testing.T) { + filename := "example2" + field := ReadField(filename) + + fmt.Println(field.SparseString()) + + graph := CreateGraph(field) + t.Log(graph) +} + +func TestPrintGraph(t *testing.T) { + filename := "example2" + field := ReadField(filename) + + fmt.Println(field.SparseString()) + + graph := CreateGraph(field) + t.Log(PrintFieldWithGraph(graph, field)) + t.Logf(">>>\n %+v\n", graph) + +} + +func TestPrintGraphInput(t *testing.T) { + filename := "input" + field := ReadField(filename) + + fmt.Println(field.SparseString()) + + graph := CreateGraph(field) + t.Log(PrintFieldWithGraph(graph, field)) + t.Logf(">>>\n %+v\n", graph) + +} + +func TestPrintMermaidGraphInput(t *testing.T) { + filename := "input" + field := ReadField(filename) + + fmt.Println(field.SparseString()) + + graph := CreateGraph(field) + mmdContent := GraphToMermaid(graph) + t.Log(mmdContent) + + fileBorder, err := os.Create(filename + ".mmd") + if err != nil { + panic(err) + } + defer func() { + if err := fileBorder.Close(); err != nil { + panic(err) + } + }() + + fileBorder.WriteString(mmdContent) +} + +func TestGraphMaxBetweenExample(t *testing.T) { + filename := "example" + field := ReadField(filename) + graph := CreateGraph(field) + + t.Log(PrintFieldWithGraph(graph, field)) + + + from := graph.nodes[Coord{Row: 0, Col: field.StartCol}] + to := graph.nodes[field.EndCoord()] + + dist := graph.DFSLenOnGraph(from, mapset.NewSet[int](), to, 0) + + t.Log(graph) + t.Logf("please dist %d", dist) + +} + +func TestGraphMaxBetweenInput(t *testing.T) { + filename := "input" + field := ReadField(filename) + + graph := CreateGraph(field) + + t.Log(PrintFieldWithGraph(graph, field)) + from := graph.nodes[Coord{Row: 0, Col: field.StartCol}] + to := graph.nodes[field.EndCoord()] + + dist := graph.DFSLenOnGraph(from, mapset.NewSet[int](), to, 0) + + t.Log(graph) + t.Logf("please dist %d", dist) + +} diff --git a/day23/input.mmd b/day23/input.mmd new file mode 100644 index 0000000..c90b0fc --- /dev/null +++ b/day23/input.mmd @@ -0,0 +1,62 @@ + +flowchart LR + M---|length 166|N + d---|length 62|h + H---|length 190|I + f---|length 136|h + j---|length 94|k + B---|length 152|L + I---|length 40|J + W---|length 56|X + E---|length 214|F + C---|length 60|K + V---|length 142|b + a---|length 110|b + I---|length 138|P + J---|length 184|K + Y---|length 146|a + c---|length 190|d + Q---|length 114|T + J---|length 240|O + C---|length 184|D + L---|length 172|M + Q---|length 140|R + Y---|length 464|k + O---|length 76|V + N---|length 102|O + K---|length 152|L + U---|length 80|c + V---|length 72|W + b---|length 202|j + A---|length 39|B + W---|length 236|a + P---|length 166|Q + e---|length 174|f + G---|length 186|R + T---|length 258|d + X---|length 142|Y + b---|length 128|c + F---|length 378|G + S---|length 108|T + N---|length 62|W + U---|length 110|V + a---|length 138|k + S---|length 234|e + d---|length 108|e + H---|length 166|Q + O---|length 158|P + M---|length 360|X + h---|length 184|i + B---|length 244|C + D---|length 96|J + D---|length 154|E + R---|length 118|S + E---|length 146|I + P---|length 128|U + T---|length 268|U + i---|length 198|j + G---|length 144|H + F---|length 102|H + f---|length 77|g + K---|length 266|N + c---|length 64|i diff --git a/day23/notes.org b/day23/notes.org index 1f570c8..d26563c 100644 --- a/day23/notes.org +++ b/day23/notes.org @@ -76,3 +76,50 @@ so 4,10 and 3,11 should be visited separately. 6,17 is where they join and the point which should have second entry ** allright, ugh. my new solution is memory hogging. maybe i can draw the stuff and it will be several neat thingies +* maybe new approach? +make a graph. with vertices of Start, End and Crossroads. +yes. +let's create a graph representation. +** so, from A to AG +i think i can do this manually now +** distances are +39 +244 +184 +154 +214 +378 +144 +190 +40 +184 +152 +172 +166 +102 +158 +166 +140 +118 +108 +268 +110 +72 +56 +142 +146 +110 +128 +190 +108 +174 +77 +1 +** again? +no, let's write code. +** didn't count all the way +2023/12/23 15:55:55 at {index:30 c:{Row:125 Col:137} name:f} to {index:31 c:{Row:140 Col:139} name:g} cur dist is 3997. |max so far 6406| +signal: interrupt +FAIL sunshine.industries/aoc2023/day23 380.499s + +tried more or less stable value, and interrupted diff --git a/day23/willHelp.mmd b/day23/willHelp.mmd new file mode 100644 index 0000000..764ae58 --- /dev/null +++ b/day23/willHelp.mmd @@ -0,0 +1,72 @@ +flowchart LR + L---|length 152|K + L---|length 172|M + U---|length 268|T + U---|length 110|V + W---|length 72|V + W---|length 56|X + a---|length 146|Y + a---|length 110|b + f---|length 174|e + f---|length 77|g + f---|length 136|h + H---|length 144|G + H---|length 190|I + T---|length 108|S + T---|length 268|U + M---|length 172|L + M---|length 166|N + F---|length 214|E + F---|length 378|G + I---|length 190|H + I---|length 40|J + A---|length 2|A + A---|length 39|B + Q---|length 166|P + Q---|length 140|R + Y---|length 142|X + Y---|length 146|a + d---|length 190|c + d---|length 108|e + e---|length 108|d + e---|length 174|f + h---|length 136|f + h---|length 184|i + J---|length 40|I + J---|length 184|K + N---|length 166|M + N---|length 102|O + X---|length 56|W + X---|length 142|Y + j---|length 198|i + j---|length 94|k + B---|length 39|A + B---|length 244|C + G---|length 378|F + G---|length 144|H + P---|length 158|O + P---|length 166|Q + D---|length 184|C + D---|length 154|E + E---|length 154|D + E---|length 214|F + K---|length 184|J + K---|length 152|L + O---|length 102|N + O---|length 158|P + R---|length 140|Q + R---|length 118|S + S---|length 118|R + S---|length 108|T + V---|length 110|U + V---|length 72|W + c---|length 128|b + c---|length 190|d + C---|length 244|B + C---|length 184|D + k---|length 94|j + i---|length 184|h + i---|length 198|j + g---|length 77|f + b---|length 110|a + b---|length 128|c diff --git a/day23/willHelp.png b/day23/willHelp.png new file mode 100644 index 0000000..8947dcf Binary files /dev/null and b/day23/willHelp.png differ