229 lines
4.2 KiB
Go
229 lines
4.2 KiB
Go
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)
|
|
}
|