mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-06-05 19:59:25 +02:00
138 lines
3.2 KiB
Go
138 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"math"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/alexchao26/advent-of-code-go/cast"
|
|
"github.com/alexchao26/advent-of-code-go/mathy"
|
|
"github.com/alexchao26/advent-of-code-go/util"
|
|
)
|
|
|
|
func main() {
|
|
var part int
|
|
flag.IntVar(&part, "part", 1, "part 1 or 2")
|
|
flag.Parse()
|
|
fmt.Println("Running part", part)
|
|
|
|
ans := cleaningRobot(util.ReadFile("./input.txt"), part)
|
|
fmt.Println("Output:", ans)
|
|
}
|
|
|
|
func cleaningRobot(input string, part int) int {
|
|
var grid [][]string
|
|
for _, l := range strings.Split(input, "\n") {
|
|
grid = append(grid, strings.Split(l, ""))
|
|
}
|
|
|
|
// bfs from every numbered cell to every other
|
|
// generate a weighted graph
|
|
var graph [][]int
|
|
for r, row := range grid {
|
|
for c, cell := range row {
|
|
if regexp.MustCompile("[0-9]").MatchString(cell) {
|
|
poi := cell
|
|
distancesFromPOI := bfsGetEdgeWeights(grid, [2]int{r, c})
|
|
// initialize graph size
|
|
if graph == nil {
|
|
for i := 0; i < len(distancesFromPOI); i++ {
|
|
graph = append(graph, make([]int, len(distancesFromPOI)))
|
|
}
|
|
}
|
|
graph[cast.ToInt(poi)] = distancesFromPOI
|
|
}
|
|
}
|
|
}
|
|
|
|
// then a recursive, backtracking dfs on that weighted graph to determine
|
|
// the shortest total path
|
|
returnToZero := part != 1
|
|
return dfs(graph, 0, map[int]bool{0: true}, returnToZero)
|
|
}
|
|
|
|
type bfsNode struct {
|
|
row, col int // 0,0 is top left
|
|
distance int
|
|
}
|
|
|
|
// allows passing through points of interest
|
|
func bfsGetEdgeWeights(grid [][]string, start [2]int) []int {
|
|
// points of interest to distance to reach them from the starting coord
|
|
poiToDistance := map[string]int{
|
|
grid[start[0]][start[1]]: 0,
|
|
}
|
|
// run until all nodes have been seen...
|
|
queue := []bfsNode{
|
|
{start[0], start[1], 0},
|
|
}
|
|
visited := map[[2]int]bool{}
|
|
for len(queue) > 0 {
|
|
front := queue[0]
|
|
queue = queue[1:]
|
|
|
|
if visited[[2]int{front.row, front.col}] {
|
|
continue
|
|
}
|
|
visited[[2]int{front.row, front.col}] = true
|
|
|
|
if regexp.MustCompile("[0-9]").MatchString(grid[front.row][front.col]) {
|
|
poiToDistance[grid[front.row][front.col]] = front.distance
|
|
}
|
|
for _, d := range dirs {
|
|
nextRow, nextCol := front.row+d[0], front.col+d[1]
|
|
// don't need to check for going out of bounds because there are walls
|
|
// surrounding everything
|
|
if grid[nextRow][nextCol] != "#" {
|
|
queue = append(queue, bfsNode{
|
|
row: nextRow,
|
|
col: nextCol,
|
|
distance: front.distance + 1,
|
|
})
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
distances := make([]int, len(poiToDistance))
|
|
for numStr, dist := range poiToDistance {
|
|
distances[cast.ToInt(numStr)] = dist
|
|
}
|
|
return distances
|
|
}
|
|
|
|
var dirs = [][2]int{
|
|
{0, -1},
|
|
{0, 1},
|
|
{1, 0},
|
|
{-1, 0},
|
|
}
|
|
|
|
func dfs(graph [][]int, entryIndex int, visited map[int]bool, returnToZero bool) (minWeightSum int) {
|
|
// if all nodes have been visited, return zero for part 1, or the distance
|
|
// from the entryIndex to the zero POI
|
|
if len(graph) == len(visited) {
|
|
if returnToZero {
|
|
return graph[entryIndex][0]
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// get the minimum distance from a recursive call
|
|
minDistance := math.MaxInt32
|
|
for i, val := range graph[entryIndex] {
|
|
if !visited[i] {
|
|
visited[i] = true
|
|
|
|
dist := val + dfs(graph, i, visited, returnToZero)
|
|
minDistance = mathy.MinInt(minDistance, dist)
|
|
|
|
delete(visited, i)
|
|
}
|
|
}
|
|
|
|
return minDistance
|
|
}
|