AoC2021/src/day19/day19.go
2021-12-20 02:04:00 +01:00

204 lines
5.5 KiB
Go

package main
import (
"AOC2021/src/helper"
"fmt"
"math"
"os"
"strings"
)
type scanner struct {
points [][3]int
position [3]int
}
func main() {
args := os.Args[1:]
input, err := helper.GetInput(args[0])
if err != nil {
fmt.Println(err)
}
var scanners []scanner
for _, row := range input {
if row[1] == '-' {
scanners = append(scanners, scanner{[][3]int{}, [3]int{}})
} else {
tmpPoint, _ := helper.MapToNumber(strings.Split(row, ","))
scanners[len(scanners)-1].points = append(scanners[len(scanners)-1].points, [3]int{tmpPoint[0], tmpPoint[1], tmpPoint[2]})
}
}
hitScanners := []int{0}
run := true
for run {
run = runUntilFirstHit(&scanners, &hitScanners)
}
inputTestResult, _ := helper.GetInput("day19TestResultPoints.txt")
resultPoints := [][3]int{}
for _, row := range inputTestResult {
tmpPoint, _ := helper.MapToNumber(strings.Split(row, ","))
resultPoints = append(resultPoints, [3]int{tmpPoint[0], tmpPoint[1], tmpPoint[2]})
}
var max float64
for i, _ := range scanners {
for j, _ := range scanners {
if i != j {
diff := diffBetweenVectors(scanners[i].position, scanners[j].position)
manhattan := math.Abs(float64(diff[0])) + math.Abs(float64(diff[1])) + math.Abs(float64(diff[2]))
if manhattan > max {
max = manhattan
}
}
}
}
fmt.Println(len(scanners[0].points))
fmt.Println(max)
}
func runUntilFirstHit(scanners *[]scanner, hitScanners *[]int) bool {
for i := 0; i < len(*scanners); i++ {
if !helper.Contains(i, *hitScanners) && runFor(scanners, 0, i) {
*hitScanners = append(*hitScanners, i)
return true
}
}
return false
}
func runFor(scanners *[]scanner, i int, j int) bool {
returnBool := checkIfScannersHaveMatchingBeacons(scanners, 0, j)
if returnBool {
pointSet := make(map[[3]int]struct{})
for _, point := range (*scanners)[0].points {
pointSet[point] = struct{}{}
}
(*scanners)[0].points = [][3]int{}
for key, _ := range pointSet {
(*scanners)[0].points = append((*scanners)[0].points, key)
}
}
return returnBool
}
func checkIfScannersHaveMatchingBeacons(scanners *[]scanner, i int, j int) bool {
scanner1 := &(*scanners)[i]
scanner2 := &(*scanners)[j]
pointMapper := getOverlappingPoints(*scanner1, *scanner2)
if len(pointMapper) >= 12 {
rotationsForScanner := getAllPossibleRotationsForScanner(*scanner2)
i, diff := getRightRotation(scanner1, pointMapper, rotationsForScanner)
scanner2.position = diff
var pointsOfScanner2inRelationToScanner1 [][3]int
for x := 0; x < len(rotationsForScanner); x++ {
point := rotationsForScanner[x][i]
pointWithDiff := [3]int{point[0] + diff[0], point[1] + diff[1], point[2] + diff[2]}
pointsOfScanner2inRelationToScanner1 = append(pointsOfScanner2inRelationToScanner1, pointWithDiff)
}
scanner1.points = append(scanner1.points, pointsOfScanner2inRelationToScanner1...)
return true
}
return false
}
func getRightRotation(scanner1 *scanner, pointMapper [][2]int, rotationsForScanner [][][3]int) (int, [3]int) {
for i := 0; i < 24; i++ {
firstDiff := diffBetweenVectors(rotationsForScanner[pointMapper[0][1]][i], scanner1.points[pointMapper[0][0]])
different := false
for n := 1; n < len(pointMapper); n++ {
rotation := rotationsForScanner[pointMapper[n][1]][i]
diff := diffBetweenVectors(rotation, scanner1.points[pointMapper[n][0]])
if firstDiff != diff {
different = true
}
}
if different == false {
return i, firstDiff
}
}
return -1, [3]int{0, 0, 0}
}
func getOverlappingPoints(sc1 scanner, sc2 scanner) (pointPairs [][2]int) {
distances0 := getallDistancesBetweenPointsForScanner(sc1)
distances1 := getallDistancesBetweenPointsForScanner(sc2)
for i, _ := range distances0 {
for j, _ := range distances1 {
if len(helper.OverlapFloatArray(distances0[i], distances1[j])) >= 11 {
pointPairs = append(pointPairs, [2]int{i, j})
}
}
}
return
}
func turn(point *[3]int) {
*point = [3]int{-point[1], point[0], point[2]}
}
func roll(point *[3]int) {
*point = [3]int{point[0], point[2], -point[1]}
}
func getAllPossibleRotationsForPoint(point *[3]int) (points [][3]int) {
rotationOrder := "RTTTRTTTRTTT"
switchRotationOrder := "RTR"
executeRotationOrder(point, rotationOrder, &points)
executeRotationOrder(point, switchRotationOrder, &[][3]int{})
executeRotationOrder(point, rotationOrder, &points)
return
}
func executeRotationOrder(point *[3]int, order string, points *[][3]int) {
for _, char := range order {
switch char {
case 'R':
roll(point)
case 'T':
turn(point)
}
*points = append(*points, *point)
}
return
}
func getAllPossibleRotationsForScanner(scanner scanner) [][][3]int {
permutations := [][][3]int{}
for _, point := range scanner.points {
permutations = append(permutations, getAllPossibleRotationsForPoint(&point))
}
return permutations
}
func getallDistancesBetweenPointsForScanner(scanner scanner) [][]float64 {
var distances [][]float64
for i, point1 := range scanner.points {
var distancesForPoint []float64
for j, point2 := range scanner.points {
if i != j {
distancesForPoint = append(distancesForPoint, distanceBetweenVectors(point1, point2))
}
}
distances = append(distances, distancesForPoint)
}
return distances
}
func distanceBetweenVectors(v1 [3]int, v2 [3]int) float64 {
var diffs [3]float64
for i, _ := range v1 {
diffs[i] = float64(v2[i] - v1[i])
}
return math.Sqrt(math.Pow(diffs[0], 2) + math.Pow(diffs[1], 2) + math.Pow(diffs[2], 2))
}
func diffBetweenVectors(v1 [3]int, v2 [3]int) [3]int {
var diff [3]int
for i, _ := range v1 {
diff[i] = v2[i] - v1[i]
}
return diff
}