adventofcode/2023/day23/main.go

197 lines
4.3 KiB
Go

package main
import (
"fmt"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
func main() {
inp := h.StdinToStringSlice()
part1(inp)
fmt.Println()
part2(inp)
}
func part1(inp []string) {
fmt.Println("# Part 1")
start, end := h.Coordinate{}, h.Coordinate{}
start.X, start.Y = findStart(inp)
end.X, end.Y = findEnd(inp)
visited := map[h.Coordinate]int{start: 0}
currentDir := S
walk(inp, start, currentDir, visited)
fmt.Println(visited[end])
}
func part2(inp []string) {
fmt.Println("# Part 2")
start, end := h.Coordinate{}, h.Coordinate{}
start.X, start.Y = findStart(inp)
end.X, end.Y = findEnd(inp)
conns := getConnections(inp)
conns[start] = true
conns[end] = true
paths := getPaths(inp, conns)
visited := make([]bool, len(conns))
visited[paths[start][0].index] = true
fmt.Println(part2Walk(inp, paths, start, end, 0, visited))
}
var (
N = h.Coordinate{X: 0, Y: -1}
E = h.Coordinate{X: 1, Y: 0}
S = h.Coordinate{X: 0, Y: 1}
W = h.Coordinate{X: -1, Y: 0}
dirs = []h.Coordinate{N, E, S, W}
badSlope = map[h.Coordinate]byte{
N: 'v', E: '<', S: '^', W: '>',
}
)
func getLeft(from h.Coordinate) h.Coordinate {
switch {
case from.Equals(N):
return W
case from.Equals(E):
return N
case from.Equals(S):
return E
default:
return S
}
}
func getRight(from h.Coordinate) h.Coordinate {
switch {
case from.Equals(N):
return E
case from.Equals(E):
return S
case from.Equals(S):
return W
default:
return N
}
}
func findStart(inp []string) (int, int) {
y := 0
for x := 0; x < len(inp[0]); x++ {
if inp[y][x] == '.' {
return x, y
}
}
return -1, -1
}
func findEnd(inp []string) (int, int) {
y := len(inp) - 1
for x := 0; x < len(inp[0]); x++ {
if inp[y][x] == '.' {
return x, y
}
}
return -1, -1
}
func isValidCoord(inp []string, c h.Coordinate) bool {
return c.Y >= 0 && c.Y < len(inp) && c.X >= 0 && c.X < len(inp[0])
}
func walk(inp []string, start, currDir h.Coordinate, visited map[h.Coordinate]int) {
curr := start
currStep := visited[curr]
for _, dir := range []h.Coordinate{currDir, getLeft(currDir), getRight(currDir)} {
next := h.Coordinate{X: (curr.X + dir.X), Y: (curr.Y + dir.Y)}
if isValidCoord(inp, next) && inp[next.Y][next.X] != '#' {
bt := inp[next.Y][next.X]
if badSlope[dir] == bt {
continue
}
if val, found := visited[next]; !found || val < currStep+1 {
visited[next] = currStep + 1
walk(inp, next, dir, visited)
}
}
}
}
func getConnections(inp []string) map[h.Coordinate]bool {
conns := map[h.Coordinate]bool{}
for y, line := range inp {
for x, bt := range line {
if bt == '#' {
continue
}
pos := h.Coordinate{X: x, Y: y}
neighbors := 0
for _, nbr := range pos.GetOrthNeighbors() {
if isValidCoord(inp, nbr) && inp[nbr.Y][nbr.X] != '#' {
neighbors++
}
}
if neighbors > 2 {
conns[pos] = true
}
}
}
return conns
}
type Path struct {
end h.Coordinate
length, index int
}
func getPaths(inp []string, conns map[h.Coordinate]bool) map[h.Coordinate][]Path {
paths := map[h.Coordinate][]Path{}
connIdx := 0
for c := range conns {
for _, d := range dirs {
curr := h.Coordinate{X: c.X + d.X, Y: c.Y + d.Y}
if isValidCoord(inp, curr) && inp[curr.Y][curr.X] != '#' {
path := getPath(inp, c, curr, d, 1, conns)
path.index = connIdx
paths[c] = append(paths[c], path)
}
}
connIdx++
}
return paths
}
func getPath(inp []string, start, curr, dir h.Coordinate, length int, conns map[h.Coordinate]bool) Path {
for _, dir := range []h.Coordinate{dir, getLeft(dir), getRight(dir)} {
next := h.Coordinate{X: curr.X + dir.X, Y: curr.Y + dir.Y}
if inp[next.Y][next.X] != '#' {
if _, found := conns[next]; found {
return Path{next, length + 1, 0}
} else {
return getPath(inp, start, next, dir, length+1, conns)
}
}
}
return Path{
end: h.Coordinate{X: -1, Y: -1},
length: 0, index: 0,
}
}
func part2Walk(inp []string, paths map[h.Coordinate][]Path, start, end h.Coordinate, step int, visited []bool) int {
var max int
for _, path := range paths[start] {
if path.end == end {
return step + path.length
}
index := paths[path.end][0].index
if !visited[index] {
visited[index] = true
max = h.Max(max, part2Walk(inp, paths, path.end, end, step+path.length, visited))
visited[index] = false
}
}
return max
}