diff --git a/README.md b/README.md index e36d791..1fb6294 100644 --- a/README.md +++ b/README.md @@ -191,3 +191,23 @@ The solution for "eighteen" is: The magnitude of the final sum is 4202 The largest magnitude of any two snail numbers is 4779 ``` + +### Day nineteen + +```sh +$ ./aoc2021 --nineteen +The solution for "nineteen" is: +nineteen part 1 answer for map +nineteen part 2 answer for map +``` + + +### Day nineteen + +```sh +$ ./aoc2021 --nineteen +The solution for "nineteen" is: +nineteen part 1 answer for mapping +nineteen part 2 answer for mapping +``` + diff --git a/main.go b/main.go index d78d0f1..9d4873f 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( "unsupervised.ca/aoc2021/four" "unsupervised.ca/aoc2021/fourteen" "unsupervised.ca/aoc2021/nine" + "unsupervised.ca/aoc2021/nineteen" "unsupervised.ca/aoc2021/one" "unsupervised.ca/aoc2021/seven" "unsupervised.ca/aoc2021/seventeen" @@ -76,6 +77,8 @@ func main() { day = seventeen.Init("seventeen/input.txt") case "eighteen": day = eighteen.Init("eighteen/input.txt") + case "nineteen": + day = nineteen.Init("nineteen/input.txt") default: fmt.Printf("%q does not have a solution.\n", flagParts[1]) help() diff --git a/nineteen/input.txt b/nineteen/input.txt new file mode 100644 index 0000000..e69de29 diff --git a/nineteen/main.go b/nineteen/main.go new file mode 100644 index 0000000..9e41221 --- /dev/null +++ b/nineteen/main.go @@ -0,0 +1,23 @@ +package nineteen + +type Nineteen struct { + mapping mapping +} + +func Init(filepath string) *Nineteen { + nineteen := &Nineteen{ + mapping: mapping{}, + } + + nineteen.mapping.load(filepath) + return nineteen +} + +func (d *Nineteen) Answer() string { + return "nineteen part 1 answer for mapping" +} + +func (d *Nineteen) FollowUp() string { + return "nineteen part 2 answer for mapping" +} + diff --git a/nineteen/mapping.go b/nineteen/mapping.go new file mode 100644 index 0000000..9ad70a7 --- /dev/null +++ b/nineteen/mapping.go @@ -0,0 +1,227 @@ +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)))) +} diff --git a/nineteen/mapping_test.go b/nineteen/mapping_test.go new file mode 100644 index 0000000..8507c70 --- /dev/null +++ b/nineteen/mapping_test.go @@ -0,0 +1,55 @@ +package nineteen + +import "testing" + +func Test_read(t *testing.T) { + m := mapping{} + if err := m.load("test_input.txt"); err != nil { + t.Log(err) + t.FailNow() + } + + if len(m.scanners) != 5 { + t.Logf("Expected 5 scanners, found %d", len(m.scanners)) + t.Fail() + } + + if len(m.scanners[0].beacons) != 25 { + t.Logf("Expected 25 beacons on scanner %d, found %d", m.scanners[0].id, len(m.scanners[0].beacons)) + t.Fail() + } + + if len(m.scanners[1].beacons) != 25 { + t.Logf("Expected 25 beacons on scanner %d, found %d", m.scanners[1].id, len(m.scanners[1].beacons)) + t.Fail() + } + + if len(m.scanners[2].beacons) != 26 { + t.Logf("Expected 26 beacons on scanner %d, found %d", m.scanners[2].id, len(m.scanners[2].beacons)) + t.Fail() + } + + if len(m.scanners[3].beacons) != 25 { + t.Logf("Expected 25 beacons on scanner %d, found %d", m.scanners[3].id, len(m.scanners[3].beacons)) + t.Fail() + } + + if len(m.scanners[4].beacons) != 26 { + t.Logf("Expected 26 beacons on scanner %d, found %d", m.scanners[4].id, len(m.scanners[4].beacons)) + t.Fail() + } +} + +func Test_initialize(t *testing.T) { + m := mapping{} + m.load("test_input.txt") + + m.initialize() + + beaconCount := len(m.beacons) + + if beaconCount != 79 { + t.Logf("Expected 79 beacons, found %d", beaconCount) + t.Fail() + } +} diff --git a/nineteen/test_input.txt b/nineteen/test_input.txt new file mode 100644 index 0000000..b596cc4 --- /dev/null +++ b/nineteen/test_input.txt @@ -0,0 +1,136 @@ +--- 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 \ No newline at end of file