package nineteen import ( "bufio" "fmt" "math" "os" "strconv" "strings" ) type offset struct { label string x int y int z int } type point struct { x int y int z int } type distance struct { first string second string magnitude float64 } type pair struct { aIndex int a distance bIndex int b distance } type beacon struct { references map[int]string } type scanner struct { id int location point distances []distance beacons []offset } type mapping struct { scanners []*scanner beacons []beacon } func (m *mapping) load(filename string) error { file, err := os.Open(filename) if err != nil { return err } defer file.Close() s := bufio.NewScanner(file) m.scanners = []*scanner{} newScanner := true var scan *scanner for s.Scan() { if s.Text() == "" { newScanner = true m.scanners = append(m.scanners, scan) continue } if newScanner { scan = &scanner{} header := strings.Split(s.Text(), " ") newScannerID, _ := strconv.Atoi(header[2]) scan.id = newScannerID newScanner = false continue } coords := strings.Split(s.Text(), ",") xVal, _ := strconv.Atoi(coords[0]) yVal, _ := strconv.Atoi(coords[1]) zVal, _ := strconv.Atoi(coords[2]) scan.beacons = append(scan.beacons, offset{ label: s.Text(), x: xVal, y: yVal, z: zVal, }) } m.scanners = append(m.scanners, scan) return nil } func (m *mapping) initialize() { // For each scanner initialize the distances between points for _, s := range m.scanners { // For each beacon in the scanner find its distance to all others for i := 0; i < len(s.beacons)-1; i++ { // We don't need to check i == j ever and don't need to do a full double loop for j := i + 1; j < len(s.beacons); j++ { s.distances = append(s.distances, distance{ first: s.beacons[i].label, second: s.beacons[j].label, magnitude: distanceMagnitude(s.beacons[i], s.beacons[j]), }) } } } // Check each scanner for overlap for i := 0; i < len(m.scanners)-1; i++ { for j := i + 1; j < len(m.scanners); j++ { overlapMagnitudes := []pair{} for _, iDist := range m.scanners[i].distances { for _, jDist := range m.scanners[j].distances { if iDist.magnitude == jDist.magnitude { overlapMagnitudes = append(overlapMagnitudes, pair{ aIndex: i, a: iDist, bIndex: j, b: jDist, }) } } } if len(overlapMagnitudes) >= 6 { fmt.Println("Overlap between scanners", i, "and", j) } if len(overlapMagnitudes) >= 6 { for _, p := range overlapMagnitudes { // Check if we have seen this on another scanner found := false var newBeacon *beacon for _, b := range m.beacons { if b.references[p.aIndex] == p.a.first { b.references[p.bIndex] = p.b.first newBeacon = &beacon{ references: map[int]string{ p.aIndex: p.a.second, p.bIndex: p.b.second, }, } found = true } else if b.references[p.aIndex] == p.a.second { b.references[p.bIndex] = p.b.second newBeacon = &beacon{ references: map[int]string{ p.aIndex: p.a.first, p.bIndex: p.b.first, }, } found = true } if b.references[p.bIndex] == p.b.first { b.references[p.aIndex] = p.a.first if found { newBeacon = nil } else { newBeacon = &beacon{ references: map[int]string{ p.aIndex: p.a.second, p.bIndex: p.b.second, }, } } found = true } else if b.references[p.bIndex] == p.b.second { b.references[p.bIndex] = p.b.second if found { newBeacon = nil } else { newBeacon = &beacon{ references: map[int]string{ p.aIndex: p.a.first, p.bIndex: p.b.first, }, } } found = true } if found { break } } if found { if newBeacon != nil { m.beacons = append(m.beacons, *newBeacon) } continue } b1 := beacon{ references: map[int]string{ p.aIndex: p.a.first, p.bIndex: p.b.first, }, } b2 := beacon{ references: map[int]string{ p.aIndex: p.a.second, p.bIndex: p.b.second, }, } m.beacons = append(m.beacons, b1, b2) } } } } } func distanceMagnitude(a offset, b offset) float64 { return math.Sqrt(float64(((b.x - a.x) * (b.x - a.x)) + ((b.y - a.y) * (b.y - a.y)) + ((b.z - a.z) * (b.z - a.z)))) }