197 lines
4.3 KiB
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
|
|
}
|