Files
advent-of-code-go/2016/day24/main.go
T

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
}