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 }