diff --git a/2021/19/go.mod b/2021/19/go.mod new file mode 100644 index 0000000..8919117 --- /dev/null +++ b/2021/19/go.mod @@ -0,0 +1,9 @@ +module AoC/2021/19 + +require AoC/2021/common v0.0.0 + +require github.com/joho/godotenv v1.4.0 // indirect + +replace AoC/2021/common v0.0.0 => ../common + +go 1.17 diff --git a/2021/19/go.sum b/2021/19/go.sum new file mode 100644 index 0000000..8c9f290 --- /dev/null +++ b/2021/19/go.sum @@ -0,0 +1,2 @@ +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= diff --git a/2021/19/main.go b/2021/19/main.go new file mode 100644 index 0000000..fe1f7c8 --- /dev/null +++ b/2021/19/main.go @@ -0,0 +1,410 @@ +package main + +import ( + aoc "AoC/2021/common" + "bufio" + "fmt" + "strconv" + "strings" +) + +type Vec struct { + X int + Y int + Z int +} + +func intcos(i int) int { + var cos int + switch i { + case 0: + cos = 1 + case 1,3: + cos = 0 + case 2: + cos = -1 + default: + panic("Invalid rotation") + } + + return cos +} + +func intsin(i int) int { + var sin int + switch i { + case 0, 2: + sin = 0 + case 1: + sin = 1 + case 3: + sin = -1 + default: + panic("Invalid rotation") + } + + return sin +} + +// We only allow 90 degree rotations +func (v Vec) rotate(r Vec) Vec { + var out Vec + + cosX := intcos(r.X) + sinX := intsin(r.X) + + cosY := intcos(r.Y) + sinY := intsin(r.Y) + + cosZ := intcos(r.Z) + sinZ := intsin(r.Z) + + Axx := cosZ*cosY + Axy := cosZ*sinY*sinX - sinZ*cosX + Axz := cosZ*sinY*cosX + sinZ*sinX + + Ayx := sinZ*cosY + Ayy := sinZ*sinY*sinX + cosZ*cosX + Ayz := sinZ*sinY*cosX - cosZ*sinX + + Azx := -sinY + Azy := cosY*sinX + Azz := cosY*cosX + + out.X = Axx*v.X + Axy*v.Y + Axz*v.Z + out.Y = Ayx*v.X + Ayy*v.Y + Ayz*v.Z + out.Z = Azx*v.X + Azy*v.Y + Azz*v.Z + + return out +} + +func (a Vec) add(b Vec) Vec { + var out Vec + + out.X = a.X + b.X + out.Y = a.Y + b.Y + out.Z = a.Z + b.Z + + return out +} + +func (a Vec) subtract(b Vec) Vec { + var out Vec + + out.X = a.X - b.X + out.Y = a.Y - b.Y + out.Z = a.Z - b.Z + + return out +} + +func (a Vec) equals(b Vec) bool { + return a.X == b.X && a.Y == b.Y && a.Z == b.Z +} + +func (a Vec) distance(b Vec) int { + x := a.X - b.X + if x < 0 { + x = -x + } + + y := a.Y - b.Y + if y < 0 { + y = -y + } + + z := a.Z - b.Z + if z < 0 { + z = -z + } + + return x + y + z +} + +type Scanner struct { + ID int + Position Vec + Beacons []Vec +} + +func mapScanners(input *bufio.Scanner) []Scanner { + var scanners []Scanner + var scanner *Scanner + for input.Scan() { + line := input.Text() + if len(line) == 0 { + continue + } + + if strings.Contains(line, "scanner") { + id := len(scanners) + scanners = append(scanners, Scanner{ID: id}) + scanner = &scanners[id] + } else { + parts := strings.Split(line, ",") + var coords [3]int + for i, p := range parts { + coords[i], _ = strconv.Atoi(p) + } + + scanner.Beacons = append(scanner.Beacons, Vec{X: coords[0], Y: coords[1], Z: coords[2]}) + } + } + + var known []*Scanner + known = append(known, &scanners[0]) + + for j := 0; j < len(known); j++ { + ref := known[j] + fmt.Printf("Scanner %d is reference\n", ref.ID) + + outer: + for i := range scanners { + for k := range known { + if scanners[i].ID == known[k].ID { + continue outer + } + } + + matcher: + // Pick a reference point for each beacon + for _, pref := range ref.Beacons { + for _, pi := range scanners[i].Beacons { + counter := 0 + + // Go through all the points in the beacons + for _, bref := range ref.Beacons { + for _, bi := range scanners[i].Beacons { + // If the distance to the respective beacon is the same it is an indaction that they might be the same point + if bref.distance(pref) == bi.distance(pi) { + counter++ + + // If we find 12 points of overlap we can assume that they overlap + if counter >= 12 { + // Since the beacons overlap compared to their respective reference point, + // we know that bref should be equal bi and pref should be equal to pi + // So we can form two vectors, these should have the same magnitude and direction + l1 := bref.subtract(pref) + l2 := bi.subtract(pi) + + var rot Vec + + // In order to find the correct rotation we just apply all 24 rotations + // and figure out which result in l1 = l2 + rotate: + for rx := 0; rx < 4; rx++ { + for ry := 0; ry < 4; ry++ { + for rz := 0; rz < 4; rz++ { + rot = Vec{rx, ry, rz} + if l1.equals(l2.rotate(rot)) { + fmt.Printf("Rotation is %v\n", rot) + break rotate + } + } + } + } + + // Update the postion of the scanner and all the beacons + scanners[i].Position = bref.subtract(bi.rotate(rot)) + for k := range scanners[i].Beacons { + scanners[i].Beacons[k] = scanners[i].Position.add(scanners[i].Beacons[k].rotate(rot)) + } + + known = append(known, &scanners[i]) + + fmt.Printf("Scanner %d is positioned at %v\n", scanners[i].ID, scanners[i].Position) + break matcher + } + } + } + } + } + } + } + } + + return scanners +} + +func main() { + challenge := aoc.New(2021, 19) + + challenge.Test(`--- 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`, []int{79, 3621}) + + challenge.Solution(1, func (input *bufio.Scanner) int { + scanners := mapScanners(input) + + var beacons []Vec + for _, scanner := range scanners { + findUnique: + for _, beacon := range scanner.Beacons { + for _, b := range beacons { + if beacon.equals(b) { + continue findUnique + } + } + + beacons = append(beacons, beacon) + } + } + + return len(beacons) + }) + + challenge.Solution(2, func (input *bufio.Scanner) int { + scanners := mapScanners(input) + + largest := 0 + for _, a := range scanners { + for _, b := range scanners { + if a.ID == b.ID { + continue + } + + distance := a.Position.distance(b.Position) + + if distance > largest { + fmt.Printf("Scanner %d and %d are the farthest apart\n", a.ID, b.ID) + largest = distance + } + } + } + + return largest + }) +}