|
@@ -0,0 +1,148 @@
|
|
1
|
+package main
|
|
2
|
+
|
|
3
|
+import (
|
|
4
|
+ "fmt"
|
|
5
|
+ "github.com/csmith/aoc-2019/common"
|
|
6
|
+ "sync"
|
|
7
|
+)
|
|
8
|
+
|
|
9
|
+func readInput(file string) (pos [3][]int64, vel [3][]int64) {
|
|
10
|
+ for _, line := range common.ReadFileAsStrings(file) {
|
|
11
|
+ var x, y, z int64
|
|
12
|
+ if _, err := fmt.Sscanf(line, "<x=%d, y=%d, z=%d>", &x, &y, &z); err != nil {
|
|
13
|
+ panic(fmt.Sprintf("unable to parse line '%v': %v", line, err))
|
|
14
|
+ }
|
|
15
|
+
|
|
16
|
+ pos[0] = append(pos[0], x)
|
|
17
|
+ pos[1] = append(pos[1], y)
|
|
18
|
+ pos[2] = append(pos[2], z)
|
|
19
|
+ vel[0] = append(vel[0], 0)
|
|
20
|
+ vel[1] = append(vel[1], 0)
|
|
21
|
+ vel[2] = append(vel[2], 0)
|
|
22
|
+ }
|
|
23
|
+ return
|
|
24
|
+}
|
|
25
|
+
|
|
26
|
+func attract(p1, p2 int64, dp1, dp2 *int64) {
|
|
27
|
+ if p1 < p2 {
|
|
28
|
+ *dp1++
|
|
29
|
+ *dp2--
|
|
30
|
+ } else if p1 > p2 {
|
|
31
|
+ *dp1--
|
|
32
|
+ *dp2++
|
|
33
|
+ }
|
|
34
|
+}
|
|
35
|
+
|
|
36
|
+func step(pos, vel []int64) {
|
|
37
|
+ for x := 0; x < len(pos); x++ {
|
|
38
|
+ for y := x + 1; y < len(pos); y++ {
|
|
39
|
+ attract(pos[x], pos[y], &vel[x], &vel[y])
|
|
40
|
+ }
|
|
41
|
+ }
|
|
42
|
+
|
|
43
|
+ for i, v := range vel {
|
|
44
|
+ pos[i] += v
|
|
45
|
+ }
|
|
46
|
+}
|
|
47
|
+
|
|
48
|
+func static(vel []int64) bool {
|
|
49
|
+ for _, v := range vel {
|
|
50
|
+ if v != 0 {
|
|
51
|
+ return false
|
|
52
|
+ }
|
|
53
|
+ }
|
|
54
|
+ return true
|
|
55
|
+}
|
|
56
|
+
|
|
57
|
+func energy(channel chan []int64, moons int) int64 {
|
|
58
|
+ sums := make([]int64, moons*2)
|
|
59
|
+ for i := 0; i < 3; i++ {
|
|
60
|
+ row := <-channel
|
|
61
|
+ for n, v := range row {
|
|
62
|
+ sums[n] += common.Abs(v)
|
|
63
|
+ }
|
|
64
|
+ }
|
|
65
|
+
|
|
66
|
+ energy := int64(0)
|
|
67
|
+ for i := 0; i < len(sums)/2; i++ {
|
|
68
|
+ energy += sums[i] * sums[len(sums)/2+i]
|
|
69
|
+ }
|
|
70
|
+ return energy
|
|
71
|
+}
|
|
72
|
+
|
|
73
|
+// Sweeping assumptions/notes on how this mess works:
|
|
74
|
+//
|
|
75
|
+// 1) the movement of the planets will be parabolic - they'll accelerate towards each other,
|
|
76
|
+// then gradually slow down to a complete stop and reverse path going back to the starting point.
|
|
77
|
+// I'm not sure I can prove that's the case in general, but it does seem to hold true.
|
|
78
|
+//
|
|
79
|
+// 2) the middle of the parabola occurs after step 1000 (otherwise the code would need a fiddly bit of
|
|
80
|
+// state tracking to ensure it returned a value for part 1).
|
|
81
|
+//
|
|
82
|
+// 3) because the axes are independent, they can be simulated in parallel, and their individual parabolic inflection
|
|
83
|
+// points found. This gives us the loop count for each axis (2x the number of steps to reach the inflection point),
|
|
84
|
+// and we can find the first time all three axis's loops intersect by finding the lowest common multiple of those
|
|
85
|
+// three values.
|
|
86
|
+//
|
|
87
|
+// e.g.
|
|
88
|
+//
|
|
89
|
+// <------------------- position on axis ------------------>
|
|
90
|
+//
|
|
91
|
+// __,..--'"" |
|
|
92
|
+// step 1000 _,..--'"" ^ |
|
|
93
|
+// v _,..-'"" start |
|
|
94
|
+// _,..-'" vel = 0 |
|
|
95
|
+// _,.-'" |
|
|
96
|
+// _.-'" |
|
|
97
|
+// _.-" |
|
|
98
|
+// .-' |
|
|
99
|
+// .' |
|
|
100
|
+// / |
|
|
101
|
+// ; } inflection point steps
|
|
102
|
+// ; } vel = 0, step = n |
|
|
103
|
+// \ |
|
|
104
|
+// `. |
|
|
105
|
+// `-. |
|
|
106
|
+// "-._ |
|
|
107
|
+// "`-._ |
|
|
108
|
+// "`-.,_ |
|
|
109
|
+// "`-..,_ |
|
|
110
|
+// ""`-..,_ |
|
|
111
|
+// ""`--..,_ |
|
|
112
|
+// ""`--..,__ V
|
|
113
|
+// ^
|
|
114
|
+// back to original position
|
|
115
|
+// vel = 0, step = 2n
|
|
116
|
+func main() {
|
|
117
|
+ pos, vel := readInput("12/input.txt")
|
|
118
|
+
|
|
119
|
+ part1wg, part1chan := &sync.WaitGroup{}, make(chan []int64, 3)
|
|
120
|
+ part2wg, part2chan := &sync.WaitGroup{}, make(chan int64, 3)
|
|
121
|
+ for i, ps := range pos {
|
|
122
|
+ part1wg.Add(1)
|
|
123
|
+ part2wg.Add(1)
|
|
124
|
+
|
|
125
|
+ go func(pos, vel []int64) {
|
|
126
|
+ for n := int64(0); ; n++ {
|
|
127
|
+ step(pos, vel)
|
|
128
|
+
|
|
129
|
+ if n+1 == 1000 {
|
|
130
|
+ part1chan <- append(pos, vel...)
|
|
131
|
+ part1wg.Done()
|
|
132
|
+ }
|
|
133
|
+
|
|
134
|
+ if static(vel) {
|
|
135
|
+ part2chan <- 2 * (n + 1)
|
|
136
|
+ part2wg.Done()
|
|
137
|
+ return
|
|
138
|
+ }
|
|
139
|
+ }
|
|
140
|
+ }(ps, vel[i])
|
|
141
|
+ }
|
|
142
|
+
|
|
143
|
+ part1wg.Wait()
|
|
144
|
+ println(energy(part1chan, len(pos[0])))
|
|
145
|
+
|
|
146
|
+ part2wg.Wait()
|
|
147
|
+ println(common.LCM(<-part2chan, <-part2chan, <-part2chan))
|
|
148
|
+}
|