adventofcode/2022/day09/main.go

203 lines
4.0 KiB
Go

package main
import (
"fmt"
"os"
"strings"
"time"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
var sleepTime = (time.Second / 5)
var minx, miny, maxx, maxy int
var part = -1
var knots = 2
func main() {
inp := h.StdinToStringSlice()
var watch bool
var partSet bool
if len(os.Args) > 1 {
for _, arg := range os.Args[1:] {
if strings.HasPrefix(arg, "-w") {
watch = true
if strings.Contains(arg, "=") {
pts := strings.Split(arg, "=")
sleepTime = (time.Second / time.Duration(h.Atoi(pts[1])))
}
} else if strings.HasPrefix(arg, "-k") {
if strings.Contains(arg, "=") {
pts := strings.Split(arg, "=")
knots = h.Atoi(pts[1])
}
} else if strings.HasPrefix(arg, "-p") {
partSet = true
if strings.Contains(arg, "=") {
pts := strings.Split(arg, "=")
part = h.Atoi(pts[1])
}
}
}
}
if part == 1 {
knots = 2
} else if part == 2 {
knots = 10
}
if !watch && !partSet {
// Run both parts
fmt.Println("# Part 1")
simulate(inp, 2, watch)
fmt.Println()
fmt.Println("# Part 2")
simulate(inp, 10, watch)
} else {
if part > 0 && part <= 2 {
fmt.Println("# Part", part)
} else {
fmt.Println("# Simulation with", knots, "knots")
}
simulate(inp, knots, watch)
}
}
func buildInstructions(inp []string) []byte {
var inst []byte
for i := range inp {
dir, count := inp[i][0], h.Atoi(inp[i][2:])
for j := 0; j < count; j++ {
inst = append(inst, dir)
}
}
return inst
}
func simulate(inp []string, knotCount int, watch bool) {
if watch {
fmt.Print(h.CLEAR_SCREEN)
}
visited := make(map[h.Coordinate]bool)
inst := buildInstructions(inp)
var knots []h.Coordinate
for i := 0; i < knotCount; i++ {
knots = append(knots, h.Coordinate{X: 0, Y: 0})
}
if watch {
printVisits(visited, knots)
}
for i, dir := range inst {
moveHead(dir, knots)
visited[knots[len(knots)-1]] = true
if watch {
time.Sleep(sleepTime)
fmt.Print(h.CLEAR_SCREEN)
fmt.Printf("# Knots %d (%d/%d)\n", knots, i, len(inst))
printVisits(visited, knots)
}
}
if watch {
time.Sleep(sleepTime)
fmt.Print(h.CLEAR_SCREEN)
}
if watch {
printVisits(visited, knots)
}
fmt.Printf("Tail visited %d positions\n", len(visited))
}
func moveHead(dir byte, knots []h.Coordinate) {
prevKnots := make([]h.Coordinate, len(knots))
copy(prevKnots, knots)
switch dir {
case 'U':
knots[0] = knots[0].North()
case 'R':
knots[0] = knots[0].East()
case 'D':
knots[0] = knots[0].South()
case 'L':
knots[0] = knots[0].West()
}
if knots[0].X < minx {
minx = knots[0].X
}
if knots[0].X > maxx {
maxx = knots[0].X
}
if knots[0].Y < miny {
miny = knots[0].Y
}
if knots[0].Y > maxy {
maxy = knots[0].Y
}
for i := 1; i < len(knots); i++ {
// Go through all knots and check if any need to move
if !knots[i].Equals(knots[i-1]) && !knots[i].Adjacent(knots[i-1]) {
if knots[i].X == knots[i-1].X {
// Same column
if knots[i].Y < knots[i-1].Y {
knots[i].Y++
} else {
knots[i].Y--
}
} else if knots[i].Y == knots[i-1].Y {
// Save row
if knots[i].X < knots[i-1].X {
knots[i].X++
} else {
knots[i].X--
}
} else {
// Not in the same row or column
if knots[i].X < knots[i-1].X {
knots[i].X++
} else {
knots[i].X--
}
if knots[i].Y < knots[i-1].Y {
knots[i].Y++
} else {
knots[i].Y--
}
}
} else {
// If one knot didn't need to move, none behind it will
break
}
}
}
func printVisits(m map[h.Coordinate]bool, knots []h.Coordinate) {
for y := miny; y <= maxy; y++ {
for x := minx; x <= maxx; x++ {
var isKnot bool
for i := range knots {
if knots[i].X == x && knots[i].Y == y {
isKnot = true
var bt byte
if i == 0 {
bt = 'H'
} else {
bt = '0' + byte(i)
}
fmt.Print(string(bt))
break
}
}
if !isKnot {
if v, ok := m[h.Coordinate{X: x, Y: y}]; ok && v {
fmt.Print("#")
} else {
fmt.Print(".")
}
}
}
fmt.Println()
}
fmt.Printf("Bridge: %s\n", knots)
fmt.Println()
}