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

268 lines
5.4 KiB
Go

// Accidentally named all the carts miners for some reason...
package main
import (
"flag"
"fmt"
"sort"
"strings"
"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)
if part == 1 {
ans := part1(util.ReadFile("./input.txt"))
fmt.Println("Output:", ans)
} else {
ans := part2(util.ReadFile("./input.txt"))
fmt.Println("Output:", ans)
}
}
func part1(input string) string {
grid, miners := parseInputs(input)
var collisionCoords map[[2]int]bool
for len(collisionCoords) == 0 {
collisionCoords = tick(grid, miners)
// fmt.Println(printGridWithMiners(grid, miners))
}
var coordString string
// should only be one entry, but range over the map to make it easier
// it's a map only because of how I wanted to handle part 2
for c := range collisionCoords {
coordString = fmt.Sprintf("%d,%d", c[1], c[0])
}
return coordString // X,Y i.e. COL, ROW
}
func part2(input string) string {
grid, miners := parseInputs(input)
for len(miners) > 1 {
collisionCoords := tick(grid, miners)
// remove all miners that have collided
if len(collisionCoords) > 0 {
// wholesale replacement of miners slice
remainingMiners := []*Miner{}
for _, m := range miners {
hasCollided := collisionCoords[m.coords]
if !hasCollided {
remainingMiners = append(remainingMiners, m)
}
}
miners = remainingMiners
}
// fmt.Println(printGridWithMiners(grid, miners))
}
finalAnsCoords := miners[0].coords
return fmt.Sprintf("%d,%d", finalAnsCoords[1], finalAnsCoords[0]) // X,Y i.e. COL, ROW
}
type Miner struct {
coords [2]int
direction string
lastTurn int
}
func (m *Miner) Move() {
switch m.direction {
case "up":
m.coords[0]--
case "down":
m.coords[0]++
case "left":
m.coords[1]--
case "right":
m.coords[1]++
}
}
// HandleIntersection is for "+" cells
func (m *Miner) HandleIntersection() {
turns := []string{"left", "straight", "right"}
m.lastTurn++
m.lastTurn %= 3
turnDirection := turns[m.lastTurn]
if turnDirection == "straight" {
// do nothing
return
}
// get new direction
directions := []string{"left", "up", "right", "down"}
var index int
for i, d := range directions {
if d == m.direction {
index = i
break
}
}
if turnDirection == "left" {
index--
} else if turnDirection == "right" {
index++
}
index %= 4
if index < 0 {
index = 3
}
m.direction = directions[index]
return
}
func parseInputs(input string) (grid [][]string, miners []*Miner) {
// overwrite miners with their corresponding grid value
minerVals := map[rune]string{
'^': "|",
'v': "|",
'>': "-",
'<': "-",
}
minerDirections := map[rune]string{
'^': "up",
'v': "down",
'>': "right",
'<': "left",
}
lines := strings.Split(input, "\n")
for r, l := range lines {
if l == "" {
continue
}
row := []string{}
// each line represents a row
for col, v := range l {
if minerVals[v] != "" {
row = append(row, minerVals[v])
miners = append(miners, &Miner{
coords: [2]int{r, col},
direction: minerDirections[v],
lastTurn: 2,
})
} else {
row = append(row, string(v))
}
}
grid = append(grid, row)
}
return grid, miners
}
// collision will not be "" if there is a collision
func tick(grid [][]string, miners []*Miner) (collisionCoords map[[2]int]bool) {
// sort miners into order of who will move first
sort.Slice(miners, func(i, j int) bool {
iCoords := miners[i].coords
jCoords := miners[j].coords
if iCoords[0] != jCoords[0] {
return iCoords[0] < jCoords[0]
}
return iCoords[1] < jCoords[1]
})
// map to detect collisions
minerCoords := map[[2]int]bool{}
// populate all coords to start because collisions don't necessarily happen
// after one or the other has moved
for _, m := range miners {
minerCoords[m.coords] = true
}
collisionCoords = make(map[[2]int]bool)
for _, m := range miners {
coords := m.coords
// if a collision has already occured here, do not move this cart
if collisionCoords[coords] {
continue
}
switch grid[coords[0]][coords[1]] {
case "/":
switch m.direction {
case "left":
m.direction = "down"
case "right":
m.direction = "up"
case "up":
m.direction = "right"
case "down":
m.direction = "left"
}
case "\\":
switch m.direction {
case "left":
m.direction = "up"
case "right":
m.direction = "down"
case "up":
m.direction = "left"
case "down":
m.direction = "right"
}
case "+":
m.HandleIntersection()
}
// remove current coordinates (as moving off this cell)
minerCoords[m.coords] = false
m.Move()
// if a Miner is at the new coordinates, add a collision entry
if minerCoords[m.coords] {
collisionCoords[m.coords] = true
}
// update coordinates in map
minerCoords[m.coords] = true
}
return collisionCoords
}
// helper func to watch the miners move
func printGridWithMiners(grid [][]string, miners []*Miner) string {
minerDirections := map[string]string{
"up": "^",
"down": "v",
"right": ">",
"left": "<",
}
str := ""
mapMinerCoords := map[[2]int]*Miner{}
for _, m := range miners {
mapMinerCoords[m.coords] = m
}
for r, row := range grid {
for c, val := range row {
if m, ok := mapMinerCoords[[2]int{r, c}]; ok {
str += minerDirections[m.direction]
} else {
str += val
}
}
str += "\n"
}
return str
}