Browse Source

Day 12...

master
Chris Smith 4 years ago
parent
commit
95921296ab
Signed by: Chris Smith <chris@chameth.com> GPG Key ID: 3A2D4BBDC4A3C9A9
4 changed files with 177 additions and 0 deletions
  1. 2
    0
      12/answers.txt
  2. 4
    0
      12/input.txt
  3. 148
    0
      12/main.go
  4. 23
    0
      common/math.go

+ 2
- 0
12/answers.txt View File

@@ -0,0 +1,2 @@
1
+13500
2
+278013787106916

+ 4
- 0
12/input.txt View File

@@ -0,0 +1,4 @@
1
+<x=0, y=4, z=0>
2
+<x=-10, y=-6, z=-14>
3
+<x=9, y=-16, z=-3>
4
+<x=6, y=-1, z=2>

+ 148
- 0
12/main.go View File

@@ -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
+}

+ 23
- 0
common/math.go View File

@@ -21,3 +21,26 @@ func Min(x, y int) int {
21 21
 	}
22 22
 	return y
23 23
 }
24
+
25
+// GCD finds the greatest common divisor (GCD) via Euclidean algorithm
26
+// Source: https://siongui.github.io/2017/06/03/go-find-lcm-by-gcd
27
+func GCD(a, b int64) int64 {
28
+	for b != 0 {
29
+		t := b
30
+		b = a % b
31
+		a = t
32
+	}
33
+	return a
34
+}
35
+
36
+// LCM finds the Least Common Multiple (LCM) via GCD
37
+// Source: https://siongui.github.io/2017/06/03/go-find-lcm-by-gcd
38
+func LCM(a, b int64, integers ...int64) int64 {
39
+	result := a * b / GCD(a, b)
40
+
41
+	for i := 0; i < len(integers); i++ {
42
+		result = LCM(result, integers[i])
43
+	}
44
+
45
+	return result
46
+}

Loading…
Cancel
Save