package main import ( "bufio" "fmt" "log" "os" "strconv" "strings" ) type MoveDir rune const ( MoveDirUp MoveDir = 'U' MoveDirDown = 'D' MoveDirLeft = 'L' MoveDirRight = 'R' ) type Path struct { Sx, Sy int Dx, Dy int Ox, Oy int } func ToPaths(moves []string) []Path { var cx, cy int paths := make([]Path, len(moves)) for i := range moves { var path Path path.Sx = cx path.Sy = cy path.Dx = cx path.Dy = cy path.Ox = cx path.Oy = cy dist, _ := strconv.Atoi(string(moves[i][1:])) switch MoveDir(moves[i][0]) { case MoveDirUp: cy += dist path.Dy = cy case MoveDirDown: cy -= dist path.Sy = cy case MoveDirLeft: cx -= dist path.Sx = cx case MoveDirRight: cx += dist path.Dx = cx } paths[i] = path } return paths } func abs(x int) int { if x > 0 { return x } return -x } type Crossing struct { X, Y int } func findCrossings(pathA, pathB []Path, debug bool) []Crossing { crossings := make([]Crossing, 0, 10) for _, a := range pathA { for _, b := range pathB { var builder strings.Builder fmt.Fprintf(&builder, "checking %+v vs %+v... ", a, b) if a.Sy == a.Dy && b.Sx == b.Dx { fmt.Fprintf(&builder, "potential") if a.Sy >= b.Sy && a.Sy <= b.Dy { fmt.Fprintf(&builder, ", in y") if b.Sx >= a.Sx && b.Sx <= a.Dx { fmt.Fprintf(&builder, ", collision! %d %d", b.Sx, a.Sy) crossings = append(crossings, Crossing{ X: b.Sx, Y: a.Sy, }) } } } else if a.Sx == a.Dx && b.Sy == b.Dy { fmt.Fprintf(&builder, "potential") if a.Sx >= b.Sx && a.Sx <= b.Dx { fmt.Fprintf(&builder, ", in x") if b.Sy >= a.Sy && b.Sy <= a.Dy { fmt.Fprintf(&builder, ", collision! %d %d", a.Sx, b.Sy) crossings = append(crossings, Crossing{ X: a.Sx, Y: b.Sy, }) } } } if debug { log.Println(builder.String()) } } } return crossings } func FindClosestCrossing(pathA, pathB []Path, debug bool) int { dist := 999999 crossings := findCrossings(pathA, pathB, debug) for _, c := range crossings { if c.X != 0 && c.Y != 0 && abs(c.X)+abs(c.Y) < dist { dist = abs(c.X) + abs(c.Y) } } return dist } func countSteps(path []Path, crossings []Crossing) map[Crossing]int { steps := make(map[Crossing]int) for _, c := range crossings { for _, p := range path { if p.Sx == p.Dx && p.Sx == c.X && c.Y >= p.Sy && c.Y <= p.Dy { steps[c] += abs(c.Y - p.Oy) break } else if p.Sy == p.Dy && p.Sy == c.Y && c.X >= p.Sx && c.X <= p.Dx { steps[c] += abs(c.X - p.Ox) break } else { steps[c] += (p.Dx - p.Sx) + (p.Dy - p.Sy) } } } return steps } func FindShortestCrossing(pathA, pathB []Path, debug bool) int { crossings := findCrossings(pathA, pathB, debug) cA := countSteps(pathA, crossings) cB := countSteps(pathB, crossings) dist := 999999 for _, c := range crossings { if c.X != 0 && c.Y != 0 && cA[c]+cB[c] < dist { dist = cA[c] + cB[c] } } return dist } func main() { input, err := os.Open("input.txt") if err != nil { log.Fatal(fmt.Errorf("could not open input file: %w", err)) } defer input.Close() scanner := bufio.NewScanner(input) scanner.Split(bufio.ScanLines) var wires [][]Path for scanner.Scan() { m := strings.Split(strings.TrimSpace(scanner.Text()), ",") wires = append(wires, ToPaths(m)) } fmt.Printf("%+v\n", wires) dist := FindClosestCrossing(wires[0], wires[1], false) fmt.Printf("Closest: %d\n", dist) dist = FindShortestCrossing(wires[0], wires[1], false) fmt.Printf("Shortest: %d\n", dist) }