Files
aoc2021/nineteen/mapping.go
2021-12-19 21:12:48 +00:00

228 lines
4.6 KiB
Go

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