package main import ( "errors" "fmt" "os" "strings" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) func main() { inp := h.StdinToStringSlice() ans1, ans2 := solve(inp) fmt.Println("# Part 1") fmt.Println(ans1) fmt.Println() fmt.Println("# Part 2") fmt.Println(ans2) } func solve(input []string) (int, int) { scanners := parseScanners(input) solved := []Scanner{scanners[0]} solved[0].absoluteCoords = solved[0].relativeCoords solved[0].fillAbsMap() // Scanners with a position we don't know yet unk := scanners[1:] // loop until we know them all for len(unk) > 0 { fmt.Print(h.CLEAR_SCREEN, "Processing...\n") c, t := len(solved), len(scanners) h.PrintProgress(c, t) fmt.Printf(" %d/%d %d%%\n", c, t, int(float64(c)/float64(t)*100)) for i, undet := range unk { ok := undet.findCoords(solved) if ok { solved = append(solved, undet) // remove the determined scanner from the unknown list copy(unk[i:], unk[i+1:]) unk = unk[:len(unk)-1] break } } } allBeacons := map[[3]int]bool{} for _, s := range solved { for c := range s.absoluteCoordsMap { allBeacons[c] = true } } var furthest int for i, s1 := range solved { for j, s2 := range solved { if i == j { continue } dist := h.Abs(s1.x-s2.x) + h.Abs(s1.y-s2.y) + h.Abs(s1.z-s2.z) if dist > furthest { furthest = dist } } } return len(allBeacons), furthest } func parseScanners(inp []string) []Scanner { var scanners []Scanner for i := range inp { if strings.HasPrefix(inp[i], "---") { end := i for ; end < len(inp); end++ { if inp[end] == "" { break } } wrk, err := NewScannerFromInput(inp[i:end]) if err == nil { scanners = append(scanners, wrk) } else { fmt.Println("Error parsing scanners") os.Exit(1) } } } return scanners } type Scanner struct { number int x, y, z int relativeCoords [][3]int rotations [][][3]int absoluteCoords [][3]int absoluteCoordsMap map[[3]int]bool } func NewScannerFromInput(inp []string) (Scanner, error) { s := Scanner{ absoluteCoordsMap: make(map[[3]int]bool), } r := strings.NewReader(inp[0]) _, err := fmt.Fscanf(r, "--- scanner %d ---", &s.number) if err != nil { return s, errors.New("No Scanner ID") } for _, v := range inp[1:] { var x, y, z int r = strings.NewReader(v) _, err := fmt.Fscanf(r, "%d,%d,%d", &x, &y, &z) if err != nil { return s, err } s.relativeCoords = append(s.relativeCoords, [3]int{x, y, z}) } s.fillRotations() return s, nil } func (s *Scanner) fillAbsMap() { s.absoluteCoordsMap = map[[3]int]bool{} if len(s.absoluteCoords) == 0 { panic(fmt.Sprintf("absolute coords not set for scanner %d", s.number)) } for _, ac := range s.absoluteCoords { s.absoluteCoordsMap[ac] = true } } // Fill in all coordinates for all beacon rotations func (s *Scanner) fillRotations() { dir1 := s.relativeCoords var dir2, dir3, dir4, dir5, dir6 [][3]int for _, c := range dir1 { x, y, z := c[0], c[1], c[2] dir2 = append(dir2, [3]int{x, -y, -z}) dir3 = append(dir3, [3]int{x, -z, y}) dir4 = append(dir4, [3]int{-y, -z, x}) dir5 = append(dir5, [3]int{-x, -z, -y}) dir6 = append(dir6, [3]int{y, -z, -x}) } sixRotations := [][][3]int{ dir1, dir2, dir3, dir4, dir5, dir6, } var finalRotations [][][3]int for _, rotation := range sixRotations { var r2, r3, r4 [][3]int for _, c := range rotation { x, y, z := c[0], c[1], c[2] r2 = append(r2, [3]int{-y, x, z}) r3 = append(r3, [3]int{-x, -y, z}) r4 = append(r4, [3]int{y, -x, z}) } finalRotations = append(finalRotations, rotation, r2, r3, r4) } s.rotations = finalRotations } // Find the absolute coordinates for this scanner, if we can. func (s *Scanner) findCoords(solved []Scanner) bool { for _, rotatedCoords := range s.rotations { for _, set := range solved { for _, absCoord := range set.absoluteCoords { for _, relativeCoord := range rotatedCoords { unsolvedAbsoluteCoords := fillAbsCoords(absCoord, relativeCoord, rotatedCoords) var matchingCount int for _, ac := range unsolvedAbsoluteCoords { if set.absoluteCoordsMap[ac] { matchingCount++ } } if matchingCount >= 12 { s.relativeCoords = rotatedCoords s.absoluteCoords = unsolvedAbsoluteCoords s.fillAbsMap() s.x = absCoord[0] - relativeCoord[0] s.y = absCoord[1] - relativeCoord[1] s.z = absCoord[2] - relativeCoord[2] return true } } } } } return false } // Assume that absolute and relative are actually the same coordinate, calc the diff of all relative Coords func fillAbsCoords(absolute, relative [3]int, relativeCoords [][3]int) [][3]int { diff := [3]int{ absolute[0] - relative[0], absolute[1] - relative[1], absolute[2] - relative[2], } var absCoords [][3]int for _, c := range relativeCoords { absCoords = append(absCoords, [3]int{ diff[0] + c[0], diff[1] + c[1], diff[2] + c[2], }) } return absCoords }