package main import ( "fmt" "io/ioutil" "strings" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) type State struct { Position h.Coordinate3d Distance int } var filename string var directions []h.Coordinate3d func main() { filename = "input" if h.GetArgNumber(1) != "" { filename = h.GetArgNumber(1) } directions = []h.Coordinate3d{{X: 0, Y: -1, Z: 0}, {X: 1, Y: 0, Z: 0}, {X: 0, Y: 1, Z: 0}, {X: -1, Y: 0, Z: 0}} part := h.GetArgNumber(2) if part != "2" { part1(filename) } else { part2(filename) } } func part1(filename string) { maze := loadMaze(filename) queue, visited := []State{{Position: maze.Start}}, map[h.Coordinate3d]bool{maze.Start: true} var st State for { st, queue = queue[0], queue[1:] for _, d := range directions { next := h.Coordinate3d{X: st.Position.X + d.X, Y: st.Position.Y + d.Y, Z: 0} if next == maze.End { fmt.Println(st.Distance + 1) return } if maze.MazeMap[next] && !visited[next] { visited[next] = true p, ok := maze.Portals[next] if ok { next = p.To } queue = append(queue, State{next, st.Distance + 1}) } } } } func part2(filename string) { maze := loadMaze(filename) queue, visited := []State{{Position: maze.Start}}, map[h.Coordinate3d]bool{{X: maze.Start.X, Y: maze.Start.Y, Z: 0}: true} var st State for { st, queue = queue[0], queue[1:] for _, d := range directions { next := h.Coordinate3d{X: st.Position.X + d.X, Y: st.Position.Y + d.Y, Z: st.Position.Z} if next == maze.End { fmt.Println(st.Distance + 1) return } if maze.MazeMap[h.Coordinate3d{X: next.X, Y: next.Y, Z: 0}] && !visited[next] { visited[next] = true p, ok := maze.Portals[h.Coordinate3d{X: next.X, Y: next.Y, Z: 0}] if ok && (st.Position.Z > 0 || !p.Outer) { next = h.Coordinate3d{X: p.To.X, Y: p.To.Y, Z: st.Position.Z} if p.Outer { next.Z-- } else { next.Z++ } visited[next] = true } queue = append(queue, State{next, st.Distance + 1}) } } } } func loadMaze(filename string) Maze { ret := Maze{ MazeMap: make(map[h.Coordinate3d]bool), Outer: Square{}, Inner: Square{}, Portals: make(map[h.Coordinate3d]Portal), } input, _ := ioutil.ReadFile(filename) lines := make([]string, 0) for _, line := range strings.Split(string(input), "\n") { lines = append(lines, line) } //outer, inner := Square{Tl: h.Coordinate3d{X: 2, Y: 2, Z: 0}, Br: h.Coordinate3d{X: len(lines[0]) - 3, Y: len(lines) - 3, Z: 0}}, Square{} // Find the outer top-left var done bool for y := 0; y < len(lines); y++ { for x := 0; x < len(lines[y]); x++ { if lines[y][x] == '#' || lines[y][x] == '.' { ret.Outer.Tl = h.Coordinate3d{X: x, Y: y, Z: 0} done = true break } } if done { break } } // Now find the outer bottom-righ done = false for y := len(lines) - 1; y > ret.Outer.Tl.Y; y-- { for x := len(lines[y]) - 1; x > ret.Outer.Tl.X; x-- { if lines[y][x] == '#' || lines[y][x] == '.' { ret.Outer.Br = h.Coordinate3d{X: x, Y: y, Z: 0} done = true break } } if done { break } } // Find the inner top-left done = false for y := ret.Outer.Tl.Y; y < ret.Outer.Br.Y; y++ { for x := ret.Outer.Tl.X; x < ret.Outer.Br.X; x++ { if lines[y][x] != '#' && lines[y][x] != '.' { // Found it. ret.Inner.Tl = h.Coordinate3d{X: x - 1, Y: y - 1, Z: 0} done = true break } } if done { break } } // Ok, now find the inner bottom-right done = false for y := ret.Outer.Br.Y - 1; y > ret.Outer.Tl.Y; y-- { for x := ret.Outer.Br.X - 1; x > ret.Outer.Tl.X; x-- { if lines[y][x] != '#' && lines[y][x] != '.' { // Found it ret.Inner.Br = h.Coordinate3d{X: x + 1, Y: y + 1, Z: 0} done = true break } } if done { break } } for y := ret.Outer.Tl.Y; y <= ret.Outer.Br.Y; y++ { for x := ret.Outer.Tl.X; x <= ret.Outer.Br.X; x++ { if lines[y][x] == '.' { ret.MazeMap[h.Coordinate3d{X: x, Y: y, Z: 0}] = true var label string var pos h.Coordinate3d var outerPortal bool if y == ret.Outer.Tl.Y { label = lines[y-2][x:x+1] + lines[y-1][x:x+1] pos = h.Coordinate3d{X: x, Y: y - 1, Z: 0} outerPortal = true } else if y == ret.Outer.Br.Y { label = lines[y+1][x:x+1] + lines[y+2][x:x+1] pos = h.Coordinate3d{X: x, Y: y + 1, Z: 0} outerPortal = true } else if x == ret.Outer.Tl.X { label = lines[y][x-2 : x] pos = h.Coordinate3d{X: x - 1, Y: y, Z: 0} outerPortal = true } else if x == ret.Outer.Br.X { label = lines[y][x+1 : x+3] pos = h.Coordinate3d{X: x + 1, Y: y, Z: 0} outerPortal = true } else if y == ret.Inner.Br.Y && x > ret.Inner.Tl.X && x < ret.Inner.Br.X { label = lines[y-2][x:x+1] + lines[y-1][x:x+1] pos = h.Coordinate3d{X: x, Y: y - 1, Z: 0} } else if y == ret.Inner.Tl.Y && x > ret.Inner.Tl.X && x < ret.Inner.Br.X { label = lines[y+1][x:x+1] + lines[y+2][x:x+1] pos = h.Coordinate3d{X: x, Y: y + 1, Z: 0} } else if x == ret.Inner.Br.X && y > ret.Inner.Tl.Y && y < ret.Inner.Br.Y { label = lines[y][x-2 : x] pos = h.Coordinate3d{X: x - 1, Y: y, Z: 0} } else if x == ret.Inner.Tl.X && y > ret.Inner.Tl.Y && y < ret.Inner.Br.Y { label = lines[y][x+1 : x+3] pos = h.Coordinate3d{X: x + 1, Y: y, Z: 0} } if label == "AA" { ret.Start = h.Coordinate3d{X: x, Y: y, Z: 0} } else if label == "ZZ" { ret.End = h.Coordinate3d{X: x, Y: y, Z: 0} } else if label != "" { ret.Portals[pos] = Portal{Label: label, From: h.Coordinate3d{X: x, Y: y, Z: 0}, Outer: outerPortal} ret.MazeMap[pos] = true } } else { // Make sure we don't overwrite anything if _, ok := ret.MazeMap[h.Coordinate3d{X: x, Y: y, Z: 0}]; !ok { ret.MazeMap[h.Coordinate3d{X: x, Y: y, Z: 0}] = false } } } } for i, p1 := range ret.Portals { for _, p2 := range ret.Portals { if p1.Label == p2.Label && p1.From != p2.From { p1.To = p2.From ret.Portals[i] = p1 } } } return ret }