Files
advent-of-code-go/2018/day20/main.go
T

159 lines
4.2 KiB
Go

package main
import (
"flag"
"fmt"
"github.com/alexchao26/advent-of-code-go/util"
)
func main() {
var part int
flag.IntVar(&part, "part", 1, "part 1 or 2")
flag.Parse()
fmt.Println("Running part", part)
ans := part1And2(util.ReadFile("./input.txt"), part)
fmt.Println("Output:", ans)
}
// The code is nearly identical for both, so part # is passed in as an arg
func part1And2(input string, part int) int {
coordsToRooms := generateRoomMap(input)
// dijkstra traverse
var furthest int // part 1
var countFarRooms int // part 2
queue := [][3]int{{0, 0, 0}} // queue node is [3]int{row, col, distance}
roomsVisited := make(map[[2]int]bool, len(coordsToRooms))
for len(queue) != 0 {
front := queue[0]
currentCoords := [2]int{front[0], front[1]}
currentDistance := front[2]
currentRoom := coordsToRooms[currentCoords]
// do not visit the same room twice
if roomsVisited[currentCoords] {
queue = queue[1:]
continue
}
// part 1 check for furthest room
if furthest < currentDistance {
furthest = currentDistance
}
// part 2 check for rooms at least 1000 doors away
if currentDistance >= 1000 {
countFarRooms++
}
roomsVisited[currentCoords] = true
if currentRoom.northDoor {
queue = append(queue, [3]int{currentCoords[0] - 1, currentCoords[1], currentDistance + 1})
}
if currentRoom.southDoor {
queue = append(queue, [3]int{currentCoords[0] + 1, currentCoords[1], currentDistance + 1})
}
if currentRoom.eastDoor {
queue = append(queue, [3]int{currentCoords[0], currentCoords[1] + 1, currentDistance + 1})
}
if currentRoom.westDoor {
queue = append(queue, [3]int{currentCoords[0], currentCoords[1] - 1, currentDistance + 1})
}
queue = queue[1:]
}
if part == 1 {
return furthest
}
return countFarRooms
}
type room struct {
coords [2]int
northDoor, eastDoor, southDoor, westDoor bool
}
// String receiver method to satisfy Stringer interface, for easier debugging
func (r room) String() string {
return fmt.Sprintf("%v: N %v S %v E %v W %v", r.coords, r.northDoor, r.southDoor, r.eastDoor, r.westDoor)
}
// returns slice of rooms that represent the ends of child paths, these need
// to be extended upon
func generateRoomMap(input string) map[[2]int]*room {
coordsToRooms := map[[2]int]*room{
[2]int{0, 0}: &room{}, // starting room, all zero values are applicable
}
iter := coordsToRooms[[2]int{0, 0}]
var stack []*room
for _, r := range input[1 : len(input)-1] {
switch dir := string(r); dir {
case "N":
nextCoords := [2]int{iter.coords[0] - 1, iter.coords[1]}
// add room to map if it's no in there already
if _, ok := coordsToRooms[nextCoords]; !ok {
coordsToRooms[nextCoords] = &room{
coords: nextCoords,
}
}
// update valid doors, next's south, current's north
nextRoom := coordsToRooms[nextCoords]
nextRoom.southDoor = true
iter.northDoor = true
// move to next room
iter = nextRoom
case "S":
nextCoords := [2]int{iter.coords[0] + 1, iter.coords[1]}
if _, ok := coordsToRooms[nextCoords]; !ok {
coordsToRooms[nextCoords] = &room{
coords: nextCoords,
}
}
nextRoom := coordsToRooms[nextCoords]
nextRoom.northDoor = true
iter.southDoor = true
iter = nextRoom
case "E":
nextCoords := [2]int{iter.coords[0], iter.coords[1] + 1}
if _, ok := coordsToRooms[nextCoords]; !ok {
coordsToRooms[nextCoords] = &room{
coords: nextCoords,
}
}
nextRoom := coordsToRooms[nextCoords]
nextRoom.westDoor = true
iter.eastDoor = true
iter = nextRoom
case "W":
nextCoords := [2]int{iter.coords[0], iter.coords[1] - 1}
if _, ok := coordsToRooms[nextCoords]; !ok {
coordsToRooms[nextCoords] = &room{
coords: nextCoords,
}
}
nextRoom := coordsToRooms[nextCoords]
nextRoom.eastDoor = true
iter.westDoor = true
iter = nextRoom
case "(":
// push onto stack
stack = append(stack, iter)
case "|":
// reset to top of stack
iter = stack[len(stack)-1]
case ")":
// backtrack and pop off stack
iter = stack[len(stack)-1]
stack = stack[:len(stack)-1]
default:
panic("unhandled character: " + string(r))
}
}
return coordsToRooms
}