-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.swift
93 lines (72 loc) · 4.1 KB
/
main.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import simd
func main() throws {
let input: [String] = try readInput(fromTestFile: false, separator: "\n\n")
let scanners = buildInitialScanners(from: input)
/// Stores the beacons for each scanner (translated into beacon 0's coordinate space)
var scannerToBeaconsMap: [Int: Set<simd_float4>] = [0: scanners[0]]
var scannersCheckedForOverlaps: Set<Int> = []
var scannerPositions: Set<simd_float4> = []
let allScannersChecked = { scannerToBeaconsMap.count == scanners.count }
while !allScannersChecked() {
/// Will get a random scanner that hasn't been checked yet
let nextScannerToCheck = scannerToBeaconsMap.first(where: { !scannersCheckedForOverlaps.contains($0.key) })!
/// Loop through scanners that are not the scanner being checked and that aren't scanners that have already been resolved
for otherScanner in 0..<scanners.count where otherScanner != nextScannerToCheck.key && !scannerToBeaconsMap.keys.contains(otherScanner) {
defer { scannersCheckedForOverlaps.insert(nextScannerToCheck.key) }
guard let matching = getMatchingBeaconsBetween(baseScanner: nextScannerToCheck.value,
nonBaseScanner: Set(scanners[otherScanner])) else { continue }
scannerToBeaconsMap[otherScanner] = matching.allTransformedNonBaseCoordsIntoBaseSpace
scannerPositions.insert(matching.scannerPosition)
print("Scanner \(otherScanner) is at \(matching.scannerPosition)")
}
}
let allBeacons = Set(scannerToBeaconsMap.values.flatMap({ $0 }))
print("Part 1:", allBeacons.count)
let scannerPairs = Array(scannerPositions).combinations(count: 2)
let maxDistanceBetweenScanners: Float = scannerPairs
.reduce(0, { max($0, abs($1[0].x - $1[1].x) + abs($1[0].y - $1[1].y) + abs($1[0].z - $1[1].z)) })
print("Part 2:", Int(maxDistanceBetweenScanners))
}
/*
Will optionally return a tuple where:
item 0 is the relative scanners position from the base
item 1 is a set containing all of the relative scanner's beacons translated to the base scanner's coordinate space
*/
// swiftlint:disable:next line_length
private func getMatchingBeaconsBetween(baseScanner: Set<simd_float4>, nonBaseScanner: Set<simd_float4>) -> (scannerPosition: simd_float4, allTransformedNonBaseCoordsIntoBaseSpace: Set<simd_float4>)? {
// Loop through all possible orientations that a scanner can have
for rotationMatrix in RotationMatrixStore.allRotationMatrices {
let rotatedBeacons: Set<simd_float4> = nonBaseScanner.reduce(into: [], { $0.insert(rotationMatrix * $1) })
var pairs: [(simd_float4, simd_float4)] = []
for a in rotatedBeacons {
for b in baseScanner {
pairs.append((a, b))
}
}
for p in pairs {
var translationMatrix = matrix_identity_float4x4
let translationColumn = simd_float4(arrayLiteral: p.1.x - p.0.x, p.1.y - p.0.y, p.1.z - p.0.z, 1)
translationMatrix[3] = translationColumn
let matchingCoordCount = rotatedBeacons.reduce(0, { baseScanner.contains(translationMatrix * $1) ? $0 + 1 : $0 })
if matchingCoordCount >= 12 {
var matrix = rotationMatrix
matrix[3] = translationColumn
let allNonBaseTransformed: Set<simd_float4> = nonBaseScanner.reduce(into: [], { $0.insert(matrix * $1) })
return (simd_float4(x: -translationColumn.x, y: -translationColumn.y, z: -translationColumn.z, w: 1), allNonBaseTransformed)
}
}
}
return nil
}
private func buildInitialScanners(from input: [String]) -> [Set<simd_float4>] {
var scanners: [Set<simd_float4>] = []
for text in input {
let coordsAsStrings = text.split(separator: "\n").suffix(from: 1)
scanners.append(Set(coordsAsStrings.map({ coord in
let coordSplit = coord.split(separator: ",")
return simd_float4(x: Float(coordSplit[0])!, y: Float(coordSplit[1])!, z: Float(coordSplit[2])!, w: 1)
})))
}
return scanners
}
Timer.time(main)