231 lines
4.7 KiB
Go
231 lines
4.7 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
intcode "git.bullercodeworks.com/brian/adventofcode/2019/intcode-processor"
|
|
helpers "git.bullercodeworks.com/brian/adventofcode/helpers"
|
|
)
|
|
|
|
var auto bool
|
|
var maze *Maze
|
|
|
|
func main() {
|
|
progFileName := "input"
|
|
auto = true
|
|
|
|
if len(os.Args) > 1 && os.Args[1] == "-manual" {
|
|
auto = false
|
|
}
|
|
prog := intcode.ReadIntCodeFile(progFileName)
|
|
maze = NewMaze()
|
|
|
|
part1(prog)
|
|
part2()
|
|
}
|
|
|
|
var all map[string]*MazeCoord
|
|
|
|
func part1(prog []int) {
|
|
p := intcode.NewProgram(prog)
|
|
go func() {
|
|
var roadTaken []helpers.Coordinate
|
|
for {
|
|
//time.Sleep(500)
|
|
//fmt.Println(helpers.CLEAR_SCREEN)
|
|
//maze.Print()
|
|
for !p.NeedsInput() {
|
|
time.Sleep(1)
|
|
}
|
|
var movingDir int
|
|
var moveToCoord *helpers.Coordinate
|
|
if auto {
|
|
var picked bool
|
|
directions := []int{DIR_N, DIR_E, DIR_S, DIR_W}
|
|
// If we have an unexplored location, try it
|
|
for _, tryDir := range directions {
|
|
v := maze.GetCoord(maze.GetDirFromBot(tryDir))
|
|
if v == MAZE_UNKNOWN {
|
|
movingDir = tryDir
|
|
picked = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !picked {
|
|
movingDir = maze.GetDirectionToLast()
|
|
if movingDir == -1 {
|
|
fmt.Println("Maze Created")
|
|
p.ForceQuit()
|
|
return
|
|
}
|
|
picked = true
|
|
}
|
|
moveToCoord = maze.GetDirFromBot(movingDir)
|
|
} else {
|
|
for movingDir == 0 {
|
|
fmt.Print("Input (vimlike): ")
|
|
reader := bufio.NewReader(os.Stdin)
|
|
inp, err := reader.ReadString('\n')
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
inp = strings.TrimSpace(inp)
|
|
switch inp {
|
|
case "h": // West
|
|
movingDir = DIR_W
|
|
moveToCoord = maze.bot.GetWestCoord()
|
|
case "j": // South
|
|
movingDir = DIR_S
|
|
moveToCoord = maze.bot.GetSouthCoord()
|
|
case "k": // North
|
|
movingDir = DIR_N
|
|
moveToCoord = maze.bot.GetNorthCoord()
|
|
case "l": // East
|
|
movingDir = DIR_E
|
|
moveToCoord = maze.bot.GetEastCoord()
|
|
}
|
|
}
|
|
}
|
|
p.Input(movingDir)
|
|
for !p.NeedsOutput() {
|
|
time.Sleep(1)
|
|
}
|
|
moveRes := p.Output()
|
|
maze.SetCoord(moveToCoord, moveRes)
|
|
switch moveRes {
|
|
case 1, 2: // Moved
|
|
roadTaken = append(roadTaken, *maze.bot)
|
|
maze.MoveBot(movingDir)
|
|
}
|
|
if p.State() == intcode.RET_DONE || p.State() == intcode.RET_ERR {
|
|
break
|
|
}
|
|
}
|
|
}()
|
|
p.Run()
|
|
//We force quit the program when the maze is built.
|
|
// Now we find the quickest path through the maze.
|
|
fmt.Println(helpers.CLEAR_SCREEN)
|
|
maze.Print()
|
|
fmt.Println("Now find shortest path")
|
|
all = make(map[string]*MazeCoord)
|
|
for k, v := range maze.maze {
|
|
if v == MAZE_EMPTY || v == MAZE_O2SYS {
|
|
c := helpers.CoordinateFromString(k)
|
|
m := &MazeCoord{coord: c}
|
|
m.Distance = helpers.MAX_INT
|
|
all[c.String()] = m
|
|
}
|
|
}
|
|
for _, v := range all {
|
|
n, ok := all[v.coord.GetNorthCoord().String()]
|
|
if ok {
|
|
v.N = n
|
|
}
|
|
e, ok := all[v.coord.GetEastCoord().String()]
|
|
if ok {
|
|
v.E = e
|
|
}
|
|
s, ok := all[v.coord.GetSouthCoord().String()]
|
|
if ok {
|
|
v.S = s
|
|
}
|
|
w, ok := all[v.coord.GetWestCoord().String()]
|
|
if ok {
|
|
v.W = w
|
|
}
|
|
}
|
|
start := all[maze.startCoord.String()]
|
|
start.Distance = 0
|
|
fmt.Println("Processing. . .")
|
|
ProcessNode(start, 0)
|
|
fmt.Println("Distance to O2:", all[maze.o2Coord.String()].Distance)
|
|
}
|
|
|
|
func ProcessNode(m *MazeCoord, steps int) {
|
|
if m.coord.String() == maze.o2Coord.String() {
|
|
return
|
|
}
|
|
for _, neighbor := range []*MazeCoord{m.N, m.E, m.S, m.W} {
|
|
if neighbor == nil {
|
|
continue
|
|
}
|
|
wrk, ok := all[neighbor.coord.String()]
|
|
if ok {
|
|
if m.Distance+1 < wrk.Distance {
|
|
wrk.Distance = m.Distance + 1
|
|
if !wrk.Visited {
|
|
ProcessNode(wrk, m.Distance+1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m.Visited = true
|
|
}
|
|
|
|
func part2() {
|
|
// We're going to reuse the visited flag for oxygen
|
|
for _, v := range all {
|
|
v.Visited = false
|
|
}
|
|
fmt.Println("Unfilled", countUnfilledSpaces())
|
|
all[maze.o2Coord.String()].Visited = true
|
|
var cnt int
|
|
for countUnfilledSpaces() > 0 {
|
|
tick()
|
|
fmt.Println("Unfilled", countUnfilledSpaces())
|
|
cnt++
|
|
}
|
|
fmt.Println("Minutes:", cnt)
|
|
}
|
|
|
|
func countUnfilledSpaces() int {
|
|
var count int
|
|
for _, v := range all {
|
|
if !v.Visited {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
func tick() bool {
|
|
// Start with the o2 coord
|
|
var toFill []*MazeCoord
|
|
for _, v := range all {
|
|
if v.N != nil && v.N.Visited {
|
|
toFill = append(toFill, v)
|
|
continue
|
|
}
|
|
if v.E != nil && v.E.Visited {
|
|
toFill = append(toFill, v)
|
|
continue
|
|
}
|
|
if v.S != nil && v.S.Visited {
|
|
toFill = append(toFill, v)
|
|
continue
|
|
}
|
|
if v.W != nil && v.W.Visited {
|
|
toFill = append(toFill, v)
|
|
continue
|
|
}
|
|
}
|
|
for k := range toFill {
|
|
all[toFill[k].coord.String()].Visited = true
|
|
}
|
|
fmt.Println("Filled", len(toFill), "spaces")
|
|
return len(toFill) > 0
|
|
}
|
|
|
|
type MazeCoord struct {
|
|
coord *helpers.Coordinate
|
|
N, S, E, W *MazeCoord
|
|
Distance int
|
|
Visited bool
|
|
}
|