203 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			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()
 | |
| }
 |