Solve day 23 part 2
parent
798797b8fe
commit
45da513db5
235
day23/main.go
235
day23/main.go
|
@ -1,3 +1,5 @@
|
|||
// This code is horrible and repetitive, I'm sorry
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -25,6 +27,11 @@ type Coordinate struct {
|
|||
Col int
|
||||
}
|
||||
|
||||
type GraphNode struct {
|
||||
Position Coordinate
|
||||
Weight int
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 2 {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s inputfile\n", os.Args[0])
|
||||
|
@ -51,28 +58,42 @@ func main() {
|
|||
panic(fmt.Sprintf("could not parse input: %s", err))
|
||||
}
|
||||
|
||||
for _, line := range grid {
|
||||
for _, char := range line {
|
||||
fmt.Printf("%c", char)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
fmt.Printf("Part 1: %d\n", part1(grid))
|
||||
fmt.Printf("Part 2: %d\n", part2(grid))
|
||||
}
|
||||
|
||||
func part1(grid [][]Tile) int {
|
||||
return solve(grid, true)
|
||||
}
|
||||
|
||||
func part2(grid [][]Tile) int {
|
||||
return solve(grid, false)
|
||||
}
|
||||
|
||||
func solve(grid [][]Tile, respectSlopes bool) int {
|
||||
startCol, err := findStartingTile(grid[0])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("could not find starting tile: %s", err))
|
||||
}
|
||||
|
||||
path := findLongestPath(
|
||||
endCol, err := findStartingTile(grid[len(grid)-1])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("could not find starting tile: %s", err))
|
||||
}
|
||||
|
||||
graph := buildCondensedGraph(
|
||||
Coordinate{Row: 0, Col: startCol},
|
||||
Coordinate{Row: len(grid) - 1, Col: endCol},
|
||||
grid,
|
||||
respectSlopes,
|
||||
)
|
||||
|
||||
return len(path) - 1
|
||||
return findLongestPath(
|
||||
Coordinate{Row: 0, Col: startCol},
|
||||
Coordinate{Row: len(grid) - 1, Col: endCol},
|
||||
grid,
|
||||
graph,
|
||||
)
|
||||
}
|
||||
|
||||
func findStartingTile(firstRow []Tile) (int, error) {
|
||||
|
@ -93,80 +114,140 @@ func findStartingTile(firstRow []Tile) (int, error) {
|
|||
return *candidate, nil
|
||||
}
|
||||
|
||||
func findLongestPath(start Coordinate, grid [][]Tile) []Coordinate {
|
||||
// position := start
|
||||
func buildCondensedGraph(start, end Coordinate, grid [][]Tile, respectSlopes bool) map[Coordinate][]GraphNode {
|
||||
inBounds := func(pos Coordinate) bool {
|
||||
return pos.Row >= 0 && pos.Row < len(grid) && pos.Col >= 0 && pos.Col < len(grid[0])
|
||||
}
|
||||
|
||||
var dfs func(Coordinate, []Coordinate) []Coordinate
|
||||
// visited := map[Coordinate]struct{}{}
|
||||
dfs = func(cursor Coordinate, path []Coordinate) []Coordinate {
|
||||
// fmt.Println(path)
|
||||
// visited[cursor] = struct{}{}
|
||||
|
||||
upNeighbor := Coordinate{Row: cursor.Row - 1, Col: cursor.Col}
|
||||
downNeighbor := Coordinate{Row: cursor.Row + 1, Col: cursor.Col}
|
||||
leftNeighbor := Coordinate{Row: cursor.Row, Col: cursor.Col - 1}
|
||||
rightNeighbor := Coordinate{Row: cursor.Row, Col: cursor.Col + 1}
|
||||
switch grid[cursor.Row][cursor.Col] {
|
||||
case TileUp:
|
||||
// _, ok := visited[upNeighbor]
|
||||
ok := slices.Contains(path, upNeighbor)
|
||||
if inBounds(upNeighbor) && !ok {
|
||||
nextPath := slices.Clone(path)
|
||||
return dfs(upNeighbor, append(nextPath, upNeighbor))
|
||||
}
|
||||
case TileDown:
|
||||
// _, ok := visited[downNeighbor]
|
||||
ok := slices.Contains(path, downNeighbor)
|
||||
if inBounds(downNeighbor) && !ok {
|
||||
nextPath := slices.Clone(path)
|
||||
return dfs(downNeighbor, append(nextPath, downNeighbor))
|
||||
}
|
||||
case TileLeft:
|
||||
ok := slices.Contains(path, leftNeighbor)
|
||||
// _, ok := visited[leftNeighbor]
|
||||
if inBounds(leftNeighbor) && !ok {
|
||||
nextPath := slices.Clone(path)
|
||||
return dfs(leftNeighbor, append(nextPath, leftNeighbor))
|
||||
}
|
||||
case TileRight:
|
||||
ok := slices.Contains(path, rightNeighbor)
|
||||
// _, ok := visited[rightNeighbor]
|
||||
if inBounds(rightNeighbor) && !ok {
|
||||
nextPath := slices.Clone(path)
|
||||
return dfs(rightNeighbor, append(nextPath, rightNeighbor))
|
||||
}
|
||||
case TileEmpty:
|
||||
neighbors := []Coordinate{upNeighbor, downNeighbor, leftNeighbor, rightNeighbor}
|
||||
longestPath := path
|
||||
for _, neighbor := range neighbors {
|
||||
if !inBounds(neighbor) {
|
||||
continue
|
||||
} else if slices.Contains(path, neighbor) {
|
||||
continue
|
||||
}
|
||||
//else if _, ok := visited[neighbor]; ok {
|
||||
// continue
|
||||
//}
|
||||
|
||||
nextPath := slices.Clone(path)
|
||||
fullPath := dfs(neighbor, append(nextPath, neighbor))
|
||||
if len(fullPath) > len(longestPath) {
|
||||
longestPath = fullPath
|
||||
}
|
||||
}
|
||||
|
||||
return longestPath
|
||||
case TileWall:
|
||||
return path
|
||||
}
|
||||
|
||||
return path
|
||||
toVisit := []Coordinate{start}
|
||||
visited := map[Coordinate]struct{}{}
|
||||
distances := map[Coordinate]int{
|
||||
start: 0,
|
||||
}
|
||||
|
||||
return dfs(start, []Coordinate{})
|
||||
intersections := map[Coordinate]struct{}{start: {}, end: {}}
|
||||
for len(toVisit) > 0 {
|
||||
visiting := toVisit[0]
|
||||
toVisit = toVisit[1:]
|
||||
visited[visiting] = struct{}{}
|
||||
|
||||
validNeighbors := []Coordinate{}
|
||||
for _, neighbor := range findNeighbors(grid, visiting, respectSlopes) {
|
||||
if !inBounds(neighbor) {
|
||||
continue
|
||||
} else if grid[neighbor.Row][neighbor.Col] == TileWall {
|
||||
continue
|
||||
}
|
||||
|
||||
distances[neighbor] = distances[visiting] + 1
|
||||
validNeighbors = append(validNeighbors, neighbor)
|
||||
}
|
||||
|
||||
if len(validNeighbors) > 2 {
|
||||
intersections[visiting] = struct{}{}
|
||||
}
|
||||
|
||||
for _, neighbor := range validNeighbors {
|
||||
if neighbor == end {
|
||||
intersections[neighbor] = struct{}{}
|
||||
}
|
||||
|
||||
if _, ok := visited[neighbor]; !ok {
|
||||
toVisit = append(
|
||||
toVisit,
|
||||
neighbor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res := map[Coordinate][]GraphNode{}
|
||||
for intersection := range intersections {
|
||||
toVisit := []Coordinate{intersection}
|
||||
visited := map[Coordinate]struct{}{}
|
||||
distances := map[Coordinate]int{
|
||||
intersection: 0,
|
||||
}
|
||||
|
||||
for len(toVisit) > 0 {
|
||||
visiting := toVisit[0]
|
||||
toVisit = toVisit[1:]
|
||||
visited[visiting] = struct{}{}
|
||||
for _, neighbor := range findNeighbors(grid, visiting, respectSlopes) {
|
||||
if !inBounds(neighbor) {
|
||||
continue
|
||||
} else if grid[neighbor.Row][neighbor.Col] == TileWall {
|
||||
continue
|
||||
}
|
||||
|
||||
distances[neighbor] = distances[visiting] + 1
|
||||
if _, ok := intersections[neighbor]; ok && neighbor != intersection {
|
||||
res[intersection] = append(
|
||||
res[intersection],
|
||||
GraphNode{Position: neighbor, Weight: distances[neighbor]},
|
||||
)
|
||||
} else if _, ok := visited[neighbor]; !ok {
|
||||
toVisit = append(toVisit, neighbor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func findLongestPath(start Coordinate, end Coordinate, grid [][]Tile, graph map[Coordinate][]GraphNode) int {
|
||||
var dfs func(Coordinate, []GraphNode) []GraphNode
|
||||
dfs = func(coordinate Coordinate, path []GraphNode) []GraphNode {
|
||||
children := graph[coordinate]
|
||||
longestPath := path
|
||||
for _, child := range children {
|
||||
if slices.ContainsFunc(path, func(node GraphNode) bool { return node.Position == child.Position }) {
|
||||
continue
|
||||
}
|
||||
|
||||
nextPath := slices.Clone(path)
|
||||
nextPath = append(nextPath, child)
|
||||
fullPath := dfs(child.Position, nextPath)
|
||||
if sumWeights(fullPath) > sumWeights(longestPath) && fullPath[len(fullPath)-1].Position == end {
|
||||
longestPath = fullPath
|
||||
}
|
||||
}
|
||||
|
||||
return longestPath
|
||||
}
|
||||
|
||||
res := dfs(start, []GraphNode{})
|
||||
|
||||
return sumWeights(res)
|
||||
}
|
||||
|
||||
func sumWeights(nodes []GraphNode) int {
|
||||
total := 0
|
||||
for _, node := range nodes {
|
||||
total += node.Weight
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
func findNeighbors(grid [][]Tile, position Coordinate, respectSlopes bool) []Coordinate {
|
||||
upNeighbor := Coordinate{Row: position.Row - 1, Col: position.Col}
|
||||
downNeighbor := Coordinate{Row: position.Row + 1, Col: position.Col}
|
||||
leftNeighbor := Coordinate{Row: position.Row, Col: position.Col - 1}
|
||||
rightNeighbor := Coordinate{Row: position.Row, Col: position.Col + 1}
|
||||
|
||||
if respectSlopes && grid[position.Row][position.Col] == TileLeft {
|
||||
return []Coordinate{leftNeighbor}
|
||||
} else if respectSlopes && grid[position.Row][position.Col] == TileRight {
|
||||
return []Coordinate{rightNeighbor}
|
||||
} else if respectSlopes && grid[position.Row][position.Col] == TileUp {
|
||||
return []Coordinate{upNeighbor}
|
||||
} else if respectSlopes && grid[position.Row][position.Col] == TileDown {
|
||||
return []Coordinate{downNeighbor}
|
||||
}
|
||||
|
||||
return []Coordinate{upNeighbor, downNeighbor, leftNeighbor, rightNeighbor}
|
||||
}
|
||||
|
||||
func parseGrid(inputLines []string) ([][]Tile, error) {
|
||||
|
|
Loading…
Reference in New Issue