Created
December 19, 2021 23:13
-
-
Save echojc/7ac68851c81a3bd99084d0266f1503c6 to your computer and use it in GitHub Desktop.
My solution for Day 19 of Advent of Code 2021.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"math" | |
"strings" | |
) | |
// ScanResult is just a type alias - we treat this as a set | |
type ScanResult map[Vector3]bool | |
// Vector3 holds a point in 3D space and has basic helper functions such as | |
// Add(), Sub(), Inverse(), and so on | |
type Vector3 struct { | |
X, Y, Z int | |
} | |
func main() { | |
scans := parseData(exampleData()) | |
//scans := parseData(actualData()) | |
// init the ocean with points from scans[0] - this will be our reference | |
// coordinate system and all other points are mapped to this | |
ocean := ScanResult{} | |
for p := range scans[0] { | |
ocean[p] = true | |
} | |
// track all scanner locations | |
scannerLocs := []Vector3{Vector3{0, 0, 0}} | |
// scans is what is left to map | |
// remove scans[0] since we have already added that to the ocean | |
scans = scans[1:] | |
// until all scans are mapped | |
for len(scans) > 0 { | |
outer: | |
// we want to keep trying to map every scan, but we don't know the order, so | |
// we just keep trying them until there is enough data in the ocean to map | |
// them all | |
for i := len(scans) - 1; i >= 0; i-- { | |
// loop backwards so we can delete from the array as we go | |
// for each scan, try all rotations to find a fit | |
for rotID := 0; rotID < 24; rotID++ { | |
// To determine a fit: | |
// - take a pair of points, one from each scan result and assume they | |
// are the same point | |
// - find the offset for the current rotation and store it | |
// - if this rotation is a fit, multiple points (>12) will have the | |
// same offset | |
offsets := map[Vector3]int{} | |
for knownPoint := range ocean { | |
for p := range scans[i] { | |
offset := p.Rotate(rotID).Sub(knownPoint) | |
offsets[offset]++ | |
} | |
} | |
for offset, count := range offsets { | |
if count >= 12 { // found a valid mapping | |
// scanner is at the inverse of the translation vector | |
scanner := offset.Inverse() | |
scannerLocs = append(scannerLocs, scanner) | |
// adjust each point in this scan relative to scans[0] and add it | |
// to the ocean for other scans to match against | |
for p := range scans[i] { | |
mappedPoint := p.Rotate(rotID).Add(scanner) | |
ocean[mappedPoint] = true | |
} | |
fmt.Println(rotID, scanner, count) | |
// done with the current scanresult, remove it | |
scans = append(scans[:i], scans[i+1:]...) | |
continue outer | |
} | |
} | |
} | |
} | |
} | |
fmt.Println("Part 1:", len(ocean)) | |
maxDist := 0 | |
for i := 0; i < len(scannerLocs); i++ { | |
for j := 1; j < len(scannerLocs); j++ { | |
dist := scannerLocs[i].Dist(scannerLocs[j]) | |
if dist > maxDist { | |
maxDist = dist | |
} | |
} | |
} | |
fmt.Println("Part 2:", maxDist) | |
} | |
func (v Vector3) Dist(w Vector3) int { | |
return int(math.Abs(float64(v.X-w.X)) + | |
math.Abs(float64(v.Y-w.Y)) + | |
math.Abs(float64(v.Z-w.Z))) | |
} | |
func (v Vector3) Add(w Vector3) Vector3 { | |
return Vector3{ | |
X: v.X + w.X, | |
Y: v.Y + w.Y, | |
Z: v.Z + w.Z, | |
} | |
} | |
func (v Vector3) Sub(w Vector3) Vector3 { | |
return Vector3{ | |
X: v.X - w.X, | |
Y: v.Y - w.Y, | |
Z: v.Z - w.Z, | |
} | |
} | |
func (v Vector3) Inverse() Vector3 { | |
return Vector3{-v.X, -v.Y, -v.Z} | |
} | |
/// Rotate takes an int 0-23 to represent one of the 24 3D rotations | |
func (v Vector3) Rotate(id int) Vector3 { | |
switch id { | |
case 0: | |
return Vector3{v.X, v.Y, v.Z} | |
case 1: | |
return Vector3{v.X, -v.Z, v.Y} | |
case 2: | |
return Vector3{v.X, -v.Y, -v.Z} | |
case 3: | |
return Vector3{v.X, v.Z, -v.Y} | |
case 4: | |
return Vector3{-v.X, -v.Y, v.Z} | |
case 5: | |
return Vector3{-v.X, -v.Z, -v.Y} | |
case 6: | |
return Vector3{-v.X, v.Y, -v.Z} | |
case 7: | |
return Vector3{-v.X, v.Z, v.Y} | |
case 8: | |
return Vector3{v.Y, v.X, -v.Z} | |
case 9: | |
return Vector3{v.Y, -v.X, v.Z} | |
case 10: | |
return Vector3{v.Y, v.Z, v.X} | |
case 11: | |
return Vector3{v.Y, -v.Z, -v.X} | |
case 12: | |
return Vector3{-v.Y, v.X, v.Z} | |
case 13: | |
return Vector3{-v.Y, -v.X, -v.Z} | |
case 14: | |
return Vector3{-v.Y, -v.Z, v.X} | |
case 15: | |
return Vector3{-v.Y, v.Z, -v.X} | |
case 16: | |
return Vector3{v.Z, v.X, v.Y} | |
case 17: | |
return Vector3{v.Z, -v.X, -v.Y} | |
case 18: | |
return Vector3{v.Z, -v.Y, v.X} | |
case 19: | |
return Vector3{v.Z, v.Y, -v.X} | |
case 20: | |
return Vector3{-v.Z, v.X, -v.Y} | |
case 21: | |
return Vector3{-v.Z, -v.X, v.Y} | |
case 22: | |
return Vector3{-v.Z, v.Y, v.X} | |
case 23: | |
return Vector3{-v.Z, -v.Y, -v.X} | |
default: | |
panic(id) | |
} | |
} | |
func actualData() []string { | |
// load your input | |
return nil | |
} | |
func exampleData() []string { | |
return []string{ | |
"--- scanner 0 ---", | |
"404,-588,-901", | |
"528,-643,409", | |
"-838,591,734", | |
"390,-675,-793", | |
"-537,-823,-458", | |
"-485,-357,347", | |
"-345,-311,381", | |
"-661,-816,-575", | |
"-876,649,763", | |
"-618,-824,-621", | |
"553,345,-567", | |
"474,580,667", | |
"-447,-329,318", | |
"-584,868,-557", | |
"544,-627,-890", | |
"564,392,-477", | |
"455,729,728", | |
"-892,524,684", | |
"-689,845,-530", | |
"423,-701,434", | |
"7,-33,-71", | |
"630,319,-379", | |
"443,580,662", | |
"-789,900,-551", | |
"459,-707,401", | |
"", | |
"--- scanner 1 ---", | |
"686,422,578", | |
"605,423,415", | |
"515,917,-361", | |
"-336,658,858", | |
"95,138,22", | |
"-476,619,847", | |
"-340,-569,-846", | |
"567,-361,727", | |
"-460,603,-452", | |
"669,-402,600", | |
"729,430,532", | |
"-500,-761,534", | |
"-322,571,750", | |
"-466,-666,-811", | |
"-429,-592,574", | |
"-355,545,-477", | |
"703,-491,-529", | |
"-328,-685,520", | |
"413,935,-424", | |
"-391,539,-444", | |
"586,-435,557", | |
"-364,-763,-893", | |
"807,-499,-711", | |
"755,-354,-619", | |
"553,889,-390", | |
"", | |
"--- scanner 2 ---", | |
"649,640,665", | |
"682,-795,504", | |
"-784,533,-524", | |
"-644,584,-595", | |
"-588,-843,648", | |
"-30,6,44", | |
"-674,560,763", | |
"500,723,-460", | |
"609,671,-379", | |
"-555,-800,653", | |
"-675,-892,-343", | |
"697,-426,-610", | |
"578,704,681", | |
"493,664,-388", | |
"-671,-858,530", | |
"-667,343,800", | |
"571,-461,-707", | |
"-138,-166,112", | |
"-889,563,-600", | |
"646,-828,498", | |
"640,759,510", | |
"-630,509,768", | |
"-681,-892,-333", | |
"673,-379,-804", | |
"-742,-814,-386", | |
"577,-820,562", | |
"", | |
"--- scanner 3 ---", | |
"-589,542,597", | |
"605,-692,669", | |
"-500,565,-823", | |
"-660,373,557", | |
"-458,-679,-417", | |
"-488,449,543", | |
"-626,468,-788", | |
"338,-750,-386", | |
"528,-832,-391", | |
"562,-778,733", | |
"-938,-730,414", | |
"543,643,-506", | |
"-524,371,-870", | |
"407,773,750", | |
"-104,29,83", | |
"378,-903,-323", | |
"-778,-728,485", | |
"426,699,580", | |
"-438,-605,-362", | |
"-469,-447,-387", | |
"509,732,623", | |
"647,635,-688", | |
"-868,-804,481", | |
"614,-800,639", | |
"595,780,-596", | |
"", | |
"--- scanner 4 ---", | |
"727,592,562", | |
"-293,-554,779", | |
"441,611,-461", | |
"-714,465,-776", | |
"-743,427,-804", | |
"-660,-479,-426", | |
"832,-632,460", | |
"927,-485,-438", | |
"408,393,-506", | |
"466,436,-512", | |
"110,16,151", | |
"-258,-428,682", | |
"-393,719,612", | |
"-211,-452,876", | |
"808,-476,-593", | |
"-575,615,604", | |
"-485,667,467", | |
"-680,325,-822", | |
"-627,-443,-432", | |
"872,-547,-609", | |
"833,512,582", | |
"807,604,487", | |
"839,-516,451", | |
"891,-625,532", | |
"-652,-548,-490", | |
"30,-46,-14", | |
} | |
} | |
func parseData(data []string) []ScanResult { | |
scans := []ScanResult{} | |
var scan ScanResult | |
for i := 0; i < len(data); i++ { | |
if data[i] == "" { | |
continue | |
} | |
if strings.Index(data[i], "scanner") >= 0 { | |
scan = ScanResult{} | |
scans = append(scans, scan) | |
continue | |
} | |
v := Vector3{} | |
fmt.Sscanf(data[i], "%d,%d,%d", &v.X, &v.Y, &v.Z) | |
scan[v] = true | |
} | |
return scans | |
} | |
func (s ScanResult) String() string { | |
var sb strings.Builder | |
sb.WriteByte('[') | |
sep := "" | |
for p := range s { | |
sb.WriteString(sep) | |
sb.WriteString(fmt.Sprintf("%v", p)) | |
sep = " " | |
} | |
sb.WriteByte(']') | |
return sb.String() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment