adventofcode/2019/day20/maze.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)
}