package main import ( "bytes" "fmt" helpers "git.bullercodeworks.com/brian/adventofcode/helpers" ) type Maze struct { start *MazeCoord end *MazeCoord maze map[string]*MazeCoord portals map[string]*Portal current *MazeCoord BotX, BotY int TopX, TopY int } func NewMaze(file string) *Maze { inp := helpers.FileToBytes(file) lines := bytes.Split(inp, []byte{'\n'}) m := Maze{ maze: make(map[string]*MazeCoord), portals: make(map[string]*Portal), } for yk, yv := range lines { for xk, xv := range yv { if xv == '.' || xv == '#' { m.maze[c(xk, yk)] = &MazeCoord{ X: xk, Y: yk, Value: xv, steps: helpers.MAX_INT, } } } } // Now hook up neighbor coords for _, v := range m.maze { if v.X < m.BotX { m.BotX = v.X } if v.X > m.TopX { m.TopX = v.X } if v.Y < m.BotY { m.BotY = v.Y } if v.Y > m.TopY { m.TopY = v.Y } var d *MazeCoord var ok bool // Hook up north if d, ok = m.maze[c(v.X, v.Y-1)]; ok { v.N = d } else if v.Value == '.' { // North Portal name := string([]byte{lines[v.Y-2][v.X], lines[v.Y-1][v.X]}) var p *Portal if p, ok = m.portals[name]; ok { v.N = m.maze[c(p.X1, p.Y1)] m.maze[c(p.X1, p.Y1)].S = v p.X2 = v.X p.Y2 = v.Y } else { m.portals[name] = &Portal{ Name: name, X1: v.X, Y1: v.Y, } } } // Hook up east if d, ok = m.maze[c(v.X+1, v.Y)]; ok { v.E = d } else if v.Value == '.' { // East Portal name := string([]byte{lines[v.Y][v.X+1], lines[v.Y][v.X+2]}) var p *Portal if p, ok = m.portals[name]; ok { v.E = m.maze[c(p.X1, p.Y1)] m.maze[c(p.X1, p.Y1)].W = v p.X2 = v.X p.Y2 = v.Y } else { m.portals[name] = &Portal{ Name: name, X1: v.X, Y1: v.Y, } } } // Hook up south if d, ok = m.maze[c(v.X, v.Y+1)]; ok { v.S = d } else if v.Value == '.' { // South Portal name := string([]byte{lines[v.Y+1][v.X], lines[v.Y+2][v.X]}) var p *Portal if p, ok = m.portals[name]; ok { v.S = m.maze[c(p.X1, p.Y1)] m.maze[c(p.X1, p.Y1)].N = v fmt.Println(v.S, "<=>", m.maze[c(p.X1, p.Y1)].N) p.X2 = v.X p.Y2 = v.Y } else { m.portals[name] = &Portal{ Name: name, X1: v.X, Y1: v.Y, } } } // Hook up west if d, ok = m.maze[c(v.X-1, v.Y)]; ok { v.W = d } else if v.Value == '.' { // West Portal name := string([]byte{lines[v.Y][v.X-2], lines[v.Y][v.X-1]}) var p *Portal if p, ok = m.portals[name]; ok { v.W = m.maze[c(p.X1, p.Y1)] m.maze[c(p.X1, p.Y1)].E = v p.X2 = v.X p.Y2 = v.Y } else { m.portals[name] = &Portal{ Name: name, X1: v.X, Y1: v.Y, } } } } st := m.portals["AA"] m.current = m.maze[c(st.X1, st.Y1)] return &m } func (m *Maze) MoveNorth() bool { if m.current.N != nil && m.current.N.Value == '.' { m.current = m.current.N return true } return false } func (m *Maze) MoveEast() bool { if m.current.E != nil && m.current.E.Value == '.' { m.current = m.current.E return true } return false } func (m *Maze) MoveSouth() bool { if m.current.S != nil && m.current.S.Value == '.' { m.current = m.current.S return true } return false } func (m *Maze) MoveWest() bool { if m.current.W != nil && m.current.W.Value == '.' { m.current = m.current.W return true } return false } func (m *Maze) Print() { for y := m.BotY; y <= m.TopY; y++ { for x := m.BotX; x <= m.TopX; x++ { if m.current.X == x && m.current.Y == y { fmt.Print("%") } else { if v, ok := m.maze[c(x, y)]; ok { fmt.Print(string(v.Value)) } else { fmt.Print(" ") } } } fmt.Println() } } func (m *Maze) GetStart() *MazeCoord { start := m.portals["AA"] return m.maze[c(start.X1, start.Y1)] } func (m *Maze) IsDone() bool { return m.IsEnd(m.current.X, m.current.Y) } func (m *Maze) GetEnd() *MazeCoord { end := m.portals["ZZ"] return m.maze[c(end.X1, end.Y1)] } func (m *Maze) IsEnd(x, y int) bool { return x == m.GetEnd().X && y == m.GetEnd().Y } type Portal struct { Name string X1, Y1, X2, Y2 int } type MazeCoord struct { X, Y int N, E, S, W *MazeCoord Value byte visited bool steps int } func c(x, y int) string { return fmt.Sprintf("[%d, %d]", x, y) }