diff --git a/2017/day24/main.go b/2017/day24/main.go new file mode 100644 index 0000000..54335ae --- /dev/null +++ b/2017/day24/main.go @@ -0,0 +1,107 @@ +package main + +import ( + "flag" + "fmt" + "strings" + + "github.com/alexchao26/advent-of-code-go/util" +) + +func main() { + var part int + flag.IntVar(&part, "part", 1, "part 1 or 2") + flag.Parse() + fmt.Println("Running part", part) + + ans := magneticMoat(util.ReadFile("./input.txt"), part) + fmt.Println("Output:", ans) +} + +func magneticMoat(input string, part int) int { + edges := getEdges(input) + + // start the bridge with a zero + bridge := [][2]int{ + {0, 0}, + } + + usedEdges := map[[2]int]bool{} + for _, edge := range edges { + usedEdges[edge] = false + } + + bestStrength, longestBridge := backtrackBridge(bridge, usedEdges) + if part == 1 { + return bestStrength + } + return calcStrengthOfBridge(longestBridge) +} + +// backtracking algo that returns strongest bridge +func backtrackBridge(bridge [][2]int, usedEdges map[[2]int]bool) (bestStrength int, longestBridge [][2]int) { + lastVal := bridge[len(bridge)-1][1] + for edge, isUsed := range usedEdges { + // skip edges that are marked as used + if !isUsed { + clonedEdge := edge + // swap the edge vals if the first doesn't match + if clonedEdge[0] != lastVal { + clonedEdge[0], clonedEdge[1] = clonedEdge[1], clonedEdge[0] + } + // if match is found, add it to bridge, mark as used + // add to strength, recurse + if clonedEdge[0] == lastVal { + bridge = append(bridge, clonedEdge) + usedEdges[edge] = true + strength := clonedEdge[0] + clonedEdge[1] + + // recurse and bestStrength and longestLength + subStrength, subLongestBridge := backtrackBridge(bridge, usedEdges) + + strength += subStrength + + // if current bridge is longest (or wins tiebreak) set the longestBridge + if len(bridge) > len(longestBridge) || + (len(bridge) == len(longestBridge) && + calcStrengthOfBridge(bridge) > calcStrengthOfBridge(longestBridge)) { + // use this hacky append to create a copy of the bridge slice + // otherwise appends could modify the underlying array + longestBridge = append([][2]int{}, bridge...) + } + // also check if a recursive call had the longest bridge, update longest + if len(subLongestBridge) > len(longestBridge) || + (len(subLongestBridge) == len(longestBridge) && + calcStrengthOfBridge(subLongestBridge) > calcStrengthOfBridge(longestBridge)) { + longestBridge = append([][2]int{}, subLongestBridge...) + } + + // backtrack + usedEdges[edge] = false + bridge = bridge[:len(bridge)-1] + if strength > bestStrength { + bestStrength = strength + } + } + } + } + + return bestStrength, longestBridge +} + +func calcStrengthOfBridge(bridge [][2]int) int { + var sum int + for _, edge := range bridge { + sum += edge[0] + edge[1] + } + return sum +} + +func getEdges(input string) (edges [][2]int) { + for _, line := range strings.Split(input, "\n") { + var pair [2]int + fmt.Sscanf(line, "%d/%d", &pair[0], &pair[1]) + edges = append(edges, pair) + } + return edges +} diff --git a/2017/day24/main_test.go b/2017/day24/main_test.go new file mode 100644 index 0000000..0dd5281 --- /dev/null +++ b/2017/day24/main_test.go @@ -0,0 +1,37 @@ +package main + +import ( + "testing" + + "github.com/alexchao26/advent-of-code-go/util" +) + +var example = `0/2 +2/2 +2/3 +3/4 +3/5 +0/1 +10/1 +9/10` + +func Test_magneticMoat(t *testing.T) { + tests := []struct { + name string + input string + part int + want int + }{ + {"example_part1", example, 1, 31}, + {"actual_part1", util.ReadFile("input.txt"), 1, 1868}, + {"example_part2", example, 2, 19}, + {"actual_part2", util.ReadFile("input.txt"), 2, 1841}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := magneticMoat(tt.input, tt.part); got != tt.want { + t.Errorf("magneticMoat() = %v, want %v", got, tt.want) + } + }) + } +}