Browse Source

Day 10

master
Chris Smith 4 years ago
parent
commit
98c38595cc
Signed by: Chris Smith <chris@chameth.com> GPG Key ID: 3A2D4BBDC4A3C9A9
4 changed files with 191 additions and 0 deletions
  1. 2
    0
      10/answers.txt
  2. 33
    0
      10/input.txt
  3. 122
    0
      10/main.go
  4. 34
    0
      10/main_test.go

+ 2
- 0
10/answers.txt View File

@@ -0,0 +1,2 @@
1
+263
2
+1110

+ 33
- 0
10/input.txt View File

@@ -0,0 +1,33 @@
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
+.#....#.###..#..##.#.##...#.#..##

+ 122
- 0
10/main.go View File

@@ -0,0 +1,122 @@
1
+package main
2
+
3
+import (
4
+	"github.com/csmith/aoc-2019/common"
5
+	"math"
6
+	"sort"
7
+)
8
+
9
+type asteroid struct {
10
+	x, y, visible int
11
+}
12
+
13
+func buildMap(input []string) []*asteroid {
14
+	var res []*asteroid
15
+	for y, line := range input {
16
+		for x, r := range line {
17
+			if r == '#' {
18
+				res = append(res, &asteroid{x: x, y: y})
19
+			}
20
+		}
21
+	}
22
+	return res
23
+}
24
+
25
+func angleBetween(asteroid1, asteroid2 *asteroid) float64 {
26
+	if asteroid1.y == asteroid2.y {
27
+		if asteroid2.x > asteroid1.x {
28
+			return math.Pi / 2
29
+		} else {
30
+			return 3 * math.Pi / 2
31
+		}
32
+	} else {
33
+		angle := math.Atan(float64(asteroid2.x-asteroid1.x) / float64(asteroid1.y-asteroid2.y))
34
+		if asteroid1.y < asteroid2.y {
35
+			angle += math.Pi
36
+		}
37
+		if angle < 0 {
38
+			angle += math.Pi * 2
39
+		}
40
+		return angle
41
+	}
42
+}
43
+
44
+func checkAngles(asteroid1 *asteroid, others []*asteroid, countVisible bool) map[float64][]*asteroid {
45
+	angles := make(map[float64][]*asteroid)
46
+	for _, asteroid2 := range others {
47
+		if asteroid2 == asteroid1 {
48
+			continue
49
+		}
50
+
51
+		angle := angleBetween(asteroid1, asteroid2)
52
+
53
+		if len(angles[angle]) == 0 && countVisible {
54
+			asteroid1.visible++
55
+			asteroid2.visible++
56
+		}
57
+
58
+		angles[angle] = append(angles[angle], asteroid2)
59
+	}
60
+	return angles
61
+}
62
+
63
+func main() {
64
+	var (
65
+		input     = common.ReadFileAsStrings("10/input.txt")
66
+		asteroids = buildMap(input)
67
+		best      *asteroid
68
+	)
69
+
70
+	for i, asteroid1 := range asteroids {
71
+		checkAngles(asteroid1, asteroids[i+1:], true)
72
+		if best == nil || asteroid1.visible > best.visible {
73
+			best = asteroid1
74
+		}
75
+	}
76
+
77
+	if best == nil {
78
+		panic("No asteroids found?")
79
+	}
80
+
81
+	println(best.visible)
82
+
83
+	targets := checkAngles(best, asteroids, false)
84
+	angles := make([]float64, 0, len(targets))
85
+	for k := range targets {
86
+		angles = append(angles, k)
87
+	}
88
+	sort.Float64s(angles)
89
+
90
+	var destroyed *asteroid
91
+	var i = 0
92
+	for n := 0; n < 200; n++ {
93
+		if len(targets[angles[i]]) == 1 {
94
+			// There's a single target at this angle, skip the angle in the future
95
+			destroyed = targets[angles[i]][0]
96
+			angles = append(angles[:i], angles[i+1:]...)
97
+		} else {
98
+			// Multiple targets exists at this angle, remove the closest and move on to the next angle
99
+			var bestDistance = math.MaxFloat64
100
+			var bestTarget = 0
101
+			for j, target := range targets[angles[i]] {
102
+				distance := math.Abs(float64(target.x-best.x)) + math.Abs(float64(target.y-best.y))
103
+				if distance < bestDistance {
104
+					bestDistance = distance
105
+					bestTarget = j
106
+				}
107
+			}
108
+
109
+			destroyed = targets[angles[i]][bestTarget]
110
+			targets[angles[i]] = append(targets[angles[i]][:bestTarget], targets[angles[i]][bestTarget+1:]...)
111
+			i++
112
+		}
113
+
114
+		i = i % len(angles)
115
+	}
116
+
117
+	if destroyed == nil {
118
+		panic("Universe doesn't make sense. Reboot and try again?")
119
+	}
120
+
121
+	println(destroyed.x*100 + destroyed.y)
122
+}

+ 34
- 0
10/main_test.go View File

@@ -0,0 +1,34 @@
1
+package main
2
+
3
+import (
4
+	"math"
5
+	"testing"
6
+)
7
+
8
+func Test_angleBetween(t *testing.T) {
9
+	type args struct {
10
+		asteroid1 *asteroid
11
+		asteroid2 *asteroid
12
+	}
13
+	tests := []struct {
14
+		name string
15
+		args args
16
+		want float64
17
+	}{
18
+		{"above", args{&asteroid{x: 5, y: 5}, &asteroid{x: 5, y: 0}}, 0},
19
+		{"below", args{&asteroid{x: 5, y: 5}, &asteroid{x: 5, y: 10}}, math.Pi},
20
+		{"right", args{&asteroid{x: 5, y: 5}, &asteroid{x: 10, y: 5}}, math.Pi / 2},
21
+		{"left", args{&asteroid{x: 5, y: 5}, &asteroid{x: 0, y: 5}}, 3 * math.Pi / 2},
22
+		{"quadrant1", args{&asteroid{x: 5, y: 5}, &asteroid{x: 10, y: 0}}, math.Pi / 4},
23
+		{"quadrant2", args{&asteroid{x: 5, y: 5}, &asteroid{x: 10, y: 10}}, 3 * math.Pi / 4},
24
+		{"quadrant3", args{&asteroid{x: 5, y: 5}, &asteroid{x: 0, y: 10}}, 5 * math.Pi / 4},
25
+		{"quadrant4", args{&asteroid{x: 5, y: 5}, &asteroid{x: 0, y: 0}}, 7 * math.Pi / 4},
26
+	}
27
+	for _, tt := range tests {
28
+		t.Run(tt.name, func(t *testing.T) {
29
+			if got := angleBetween(tt.args.asteroid1, tt.args.asteroid2); got != tt.want {
30
+				t.Errorf("angleBetween() = %v, want %v", got, tt.want)
31
+			}
32
+		})
33
+	}
34
+}

Loading…
Cancel
Save