-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay12.swift
128 lines (94 loc) · 3.18 KB
/
Day12.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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import Foundation
import Tools
final class Day12Solver: DaySolver {
let dayNumber: Int = 12
struct Input {
let moons: [Moon]
}
struct Moon: Equatable, Hashable {
var position: Point3D
var velocity: Point3D = .init()
var potentialEnergy: Int {
abs(position.x) + abs(position.y) + abs(position.z)
}
var kineticEnergy: Int {
abs(velocity.x) + abs(velocity.y) + abs(velocity.z)
}
var energy: Int {
potentialEnergy * kineticEnergy
}
}
private func updateMoons(moons: [Moon]) -> [Moon] {
var updatedMoons = moons
for outerMoonIndex in 0 ..< moons.count {
var velocity = moons[outerMoonIndex].velocity
var position = moons[outerMoonIndex].position
for innerMoonIndex in 0 ..< moons.count where outerMoonIndex != innerMoonIndex {
let delta = moons[innerMoonIndex].position - moons[outerMoonIndex].position
velocity.x += delta.x.sign
velocity.y += delta.y.sign
velocity.z += delta.z.sign
}
position += velocity
updatedMoons[outerMoonIndex] = .init(position: position, velocity: velocity)
}
return updatedMoons
}
func solvePart1(withInput input: Input) -> Int {
var moons = input.moons
for _ in 0 ..< 1000 {
moons = updateMoons(moons: moons)
}
return moons.map(\.energy).reduce(0, +)
}
func solvePart2(withInput input: Input) -> Int {
var moons = input.moons
// the axis do not influence each other so we can focus on individual axis and find the least common multiplier for the 3 counters to see where they align
var stateXCounter = 0
var stateYCounter = 0
var stateZCounter = 0
let startState = input.moons
var counter = 0
firstMatchLoop: while true {
moons = updateMoons(moons: moons)
counter += 1
var allXSameState = true
var allYSameState = true
var allZSameState = true
for moonIndex in 0 ..< moons.count {
if moons[moonIndex].position.x != startState[moonIndex].position.x || moons[moonIndex].velocity.x != startState[moonIndex].velocity.x {
allXSameState = false
}
if moons[moonIndex].position.y != startState[moonIndex].position.y || moons[moonIndex].velocity.y != startState[moonIndex].velocity.y {
allYSameState = false
}
if moons[moonIndex].position.z != startState[moonIndex].position.z || moons[moonIndex].velocity.z != startState[moonIndex].velocity.z {
allZSameState = false
}
}
if allXSameState, stateXCounter == 0 {
stateXCounter = counter
}
if allYSameState, stateYCounter == 0 {
stateYCounter = counter
}
if allZSameState, stateZCounter == 0 {
stateZCounter = counter
}
if stateXCounter != 0, stateYCounter != 0, stateZCounter != 0 {
break
}
}
return Math.leastCommonMultiple(for: [stateXCounter, stateYCounter, stateZCounter])
}
func parseInput(rawString: String) -> Input {
let moons: [Moon] = rawString.allLines().map { line in
let rawCoordinates = line[1 ..< line.count - 1].components(separatedBy: ", ")
let x = Int(rawCoordinates[0].components(separatedBy: "=")[1])!
let y = Int(rawCoordinates[1].components(separatedBy: "=")[1])!
let z = Int(rawCoordinates[2].components(separatedBy: "=")[1])!
return .init(position: .init(x: x, y: y, z: z))
}
return .init(moons: moons)
}
}