Browse Source

Super optimise day 3.

master
Chris Smith 4 years ago
parent
commit
5937350d31
Signed by: Chris Smith <chris@chameth.com> GPG Key ID: 3A2D4BBDC4A3C9A9
1 changed files with 91 additions and 62 deletions
  1. 91
    62
      03/main.go

+ 91
- 62
03/main.go View File

@@ -7,81 +7,85 @@ import (
7 7
 	"strings"
8 8
 )
9 9
 
10
-var directions = map[byte]common.Point{
11
-	'L': {X: -1, Y: 0},
12
-	'R': {X: +1, Y: 0},
13
-	'U': {X: 0, Y: -1},
14
-	'D': {X: 0, Y: +1},
10
+type line struct {
11
+	from  int64
12
+	to    int64
13
+	perp  int64
14
+	steps int64
15 15
 }
16 16
 
17
-// buildMap constructs a map of points that one or more wires hit, to a slice
18
-// of ints representing the number of steps each wire takes to reach that point.
19
-// The number of steps is 0 if the point is the origin or if the wire doesn't
20
-// reach that point.
21
-func buildMap(origin common.Point, wires []string) map[common.Point][]int64 {
22
-	points := make(map[common.Point][]int64, 1000000)
23
-
24
-	for n, wire := range wires {
25
-		var (
26
-			pos         = origin
27
-			steps int64 = 0
28
-			moves       = strings.Split(wire, ",")
29
-		)
30
-
31
-		for _, move := range moves {
32
-			dir := directions[move[0]]
33
-			length := common.MustAtoi(move[1:])
34
-
35
-			for i := 0; i < length; i++ {
36
-				pos = pos.Plus(dir)
37
-				steps++
38
-
39
-				val, ok := points[pos]
40
-
41
-				if !ok {
42
-					points[pos] = make([]int64, len(wires))
43
-					val = points[pos]
44
-				}
45
-				val[n] = steps
46
-			}
47
-		}
48
-	}
49
-
50
-	return points
51
-}
52
-
53
-// combinedSteps computes the total number of steps each wire had to take, given a slice of measurements. If not all
54
-// wires have measurement, the second return parameter will be false.
55
-func combinedSteps(steps []int64) (int64, bool) {
56
-	var res int64 = 0
57
-	for _, distance := range steps {
58
-		if distance == 0 {
59
-			return 0, false
60
-		} else {
61
-			res += distance
17
+// readWire reads the instructions for a wire and populates the given slices with the horizontal and vertical lines
18
+// that make up the wire's path.
19
+func readWire(wire string, horizontal *[]line, vertical *[]line) {
20
+	moves := strings.Split(wire, ",")
21
+	x := int64(0)
22
+	y := int64(0)
23
+	steps := int64(0)
24
+
25
+	for _, move := range moves {
26
+		length := int64(common.MustAtoi(move[1:]))
27
+		switch move[0] {
28
+		case 'U':
29
+			*vertical = append(*vertical, line{from: y, to: y - length, perp: x, steps: steps})
30
+			y -= length
31
+		case 'D':
32
+			*vertical = append(*vertical, line{from: y, to: y + length, perp: x, steps: steps})
33
+			y += length
34
+		case 'L':
35
+			*horizontal = append(*horizontal, line{from: x, to: x - length, perp: y, steps: steps})
36
+			x -= length
37
+		case 'R':
38
+			*horizontal = append(*horizontal, line{from: x, to: x + length, perp: y, steps: steps})
39
+			x += length
62 40
 		}
41
+		steps += length
63 42
 	}
64
-	return res, true
65 43
 }
66 44
 
67
-func traceWires() (int64, int64) {
68
-	wires := common.ReadFileAsStrings("03/input.txt")
69
-	origin := common.Point{X: 0, Y: 0}
70
-	points := buildMap(origin, wires)
71
-
45
+// checkCrosses checks if any of the given sets of perpendicular lines cross, and returns the
46
+// smallest Manhattan distance to the origin, and the smallest number of combined steps, of
47
+// those crosses.
48
+func checkCrosses(horizontal *[]line, vertical *[]line) (int64, int64) {
72 49
 	var (
73 50
 		bestDistance int64 = math.MaxInt64
74 51
 		bestSteps    int64 = math.MaxInt64
75 52
 	)
76 53
 
77
-	for pos, v := range points {
78
-		if combinedSteps, hitAll := combinedSteps(v); hitAll {
79
-			if distance := pos.Manhattan(origin); distance < bestDistance {
80
-				bestDistance = distance
54
+	for _, h := range *horizontal {
55
+		for _, v := range *vertical {
56
+			var steps int64 = math.MaxInt64
57
+			if h.from <= v.perp && v.perp <= h.to {
58
+				// If the horizontal line goes left-to-right
59
+				if v.from <= h.perp && h.perp <= v.to {
60
+					// If the vertical line goes top-to-bottom
61
+					steps = (h.steps + v.perp - h.from) + (v.steps + h.perp - v.from)
62
+				} else if v.to <= h.perp && h.perp <= v.from {
63
+					// If the vertical line goes bottom-to-top
64
+					steps = (h.steps + v.perp - h.from) + (v.steps + v.from - h.perp)
65
+				} else {
66
+					continue
67
+				}
68
+			} else if h.to <= v.perp && v.perp <= h.from {
69
+				// If the horizontal line goes right-to-left
70
+				if v.from <= h.perp && h.perp <= v.to {
71
+					// If the vertical line goes top-to-bottom
72
+					steps = (h.steps + h.from - v.perp) + (v.steps + h.perp - v.from)
73
+				} else if v.to <= h.perp && h.perp <= v.from {
74
+					// If the vertical line goes bottom-to-top
75
+					steps = (h.steps + h.from - v.perp) + (v.steps + v.from - h.perp)
76
+				} else {
77
+					continue
78
+				}
79
+			} else {
80
+				continue
81 81
 			}
82 82
 
83
-			if combinedSteps < bestSteps {
84
-				bestSteps = combinedSteps
83
+			distance := common.Abs(v.perp) + common.Abs(h.perp)
84
+			if distance < bestDistance {
85
+				bestDistance = distance
86
+			}
87
+			if steps < bestSteps {
88
+				bestSteps = steps
85 89
 			}
86 90
 		}
87 91
 	}
@@ -89,8 +93,33 @@ func traceWires() (int64, int64) {
89 93
 	return bestDistance, bestSteps
90 94
 }
91 95
 
96
+func traceWires() (int64, int64) {
97
+	var (
98
+		horiz1 []line
99
+		horiz2 []line
100
+		vert1  []line
101
+		vert2  []line
102
+	)
103
+
104
+	wires := common.ReadFileAsStrings("03/input.txt")
105
+	readWire(wires[0], &horiz1, &vert1)
106
+	readWire(wires[1], &horiz2, &vert2)
107
+
108
+	d1, s1 := checkCrosses(&horiz1, &vert2)
109
+	d2, s2 := checkCrosses(&horiz2, &vert1)
110
+	return min(d1, d2), min(s1, s2)
111
+}
112
+
92 113
 func main() {
93 114
 	part1, part2 := traceWires()
94 115
 	fmt.Println(part1)
95 116
 	fmt.Println(part2)
96 117
 }
118
+
119
+func min(a, b int64) int64 {
120
+	if a < b {
121
+		return a
122
+	} else {
123
+		return b
124
+	}
125
+}

Loading…
Cancel
Save