adventofcode/2019/day15/main.go

231 lines
4.7 KiB
Go
Raw Permalink Normal View History

2019-12-15 14:57:44 +00:00
package main
import (
"bufio"
"fmt"
"os"
"strings"
"time"
intcode "git.bullercodeworks.com/brian/adventofcode/2019/intcode-processor"
2019-12-16 23:31:11 +00:00
helpers "git.bullercodeworks.com/brian/adventofcode/helpers"
2019-12-15 14:57:44 +00:00
)
var auto bool
2019-12-16 23:31:11 +00:00
var maze *Maze
2019-12-15 14:57:44 +00:00
func main() {
progFileName := "input"
2019-12-18 22:44:05 +00:00
auto = true
2019-12-15 14:57:44 +00:00
2019-12-18 22:44:05 +00:00
if len(os.Args) > 1 && os.Args[1] == "-manual" {
auto = false
2019-12-15 14:57:44 +00:00
}
prog := intcode.ReadIntCodeFile(progFileName)
2019-12-16 23:31:11 +00:00
maze = NewMaze()
2019-12-18 22:44:05 +00:00
part1(prog)
part2()
2019-12-15 14:57:44 +00:00
}
2019-12-18 22:44:05 +00:00
var all map[string]*MazeCoord
func part1(prog []int) {
2019-12-15 14:57:44 +00:00
p := intcode.NewProgram(prog)
go func() {
2019-12-16 23:31:11 +00:00
var roadTaken []helpers.Coordinate
2019-12-15 14:57:44 +00:00
for {
2019-12-18 22:44:05 +00:00
//time.Sleep(500)
//fmt.Println(helpers.CLEAR_SCREEN)
//maze.Print()
2019-12-15 14:57:44 +00:00
for !p.NeedsInput() {
time.Sleep(1)
}
2019-12-16 23:31:11 +00:00
var movingDir int
var moveToCoord *helpers.Coordinate
2019-12-15 14:57:44 +00:00
if auto {
2019-12-16 23:31:11 +00:00
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)
2019-12-15 14:57:44 +00:00
} else {
2019-12-16 23:31:11 +00:00
for movingDir == 0 {
2019-12-15 14:57:44 +00:00
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 {
2019-12-16 23:31:11 +00:00
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()
2019-12-15 14:57:44 +00:00
}
}
}
2019-12-16 23:31:11 +00:00
p.Input(movingDir)
2019-12-15 14:57:44 +00:00
for !p.NeedsOutput() {
time.Sleep(1)
}
moveRes := p.Output()
2019-12-16 23:31:11 +00:00
maze.SetCoord(moveToCoord, moveRes)
2019-12-15 14:57:44 +00:00
switch moveRes {
2019-12-16 23:31:11 +00:00
case 1, 2: // Moved
roadTaken = append(roadTaken, *maze.bot)
maze.MoveBot(movingDir)
}
if p.State() == intcode.RET_DONE || p.State() == intcode.RET_ERR {
break
2019-12-15 14:57:44 +00:00
}
}
}()
2019-12-18 13:25:25 +00:00
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")
2019-12-18 22:44:05 +00:00
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
}
2019-12-15 14:57:44 +00:00
}
2019-12-18 22:44:05 +00:00
start := all[maze.startCoord.String()]
start.Distance = 0
fmt.Println("Processing. . .")
ProcessNode(start, 0)
fmt.Println("Distance to O2:", all[maze.o2Coord.String()].Distance)
2019-12-18 13:25:25 +00:00
}
2019-12-18 22:44:05 +00:00
func ProcessNode(m *MazeCoord, steps int) {
if m.coord.String() == maze.o2Coord.String() {
return
2019-12-18 13:25:25 +00:00
}
2019-12-18 22:44:05 +00:00
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)
2019-12-18 13:25:25 +00:00
}
}
}
}
2019-12-18 22:44:05 +00:00
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
2019-12-15 14:57:44 +00:00
}