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() }