SHOW:
|
|
- or go back to the newest paste.
1 | package main | |
2 | ||
3 | import ( | |
4 | "aoc/utils" | |
5 | "fmt" | |
6 | "strings" | |
7 | ) | |
8 | ||
9 | func main() { | |
10 | g := NewGraph(utils.ReadLines()) | |
11 | fmt.Println("part1: ", g.Part1()) | |
12 | fmt.Println("part2: ", g.Part2()) | |
13 | } | |
14 | ||
15 | type Graph struct { | |
16 | edges [][]uint16 | |
17 | bigCaves uint16 | |
18 | } | |
19 | ||
20 | func (g *Graph) Part1() int { | |
21 | initCache() | |
22 | return g.countPathsPart1(startCave, 0) | |
23 | } | |
24 | ||
25 | func (g *Graph) Part2() int { | |
26 | initCache() | |
27 | return g.countPathsPart2(startCave, 0) | |
28 | } | |
29 | ||
30 | const ( | |
31 | startCave = 1 | |
32 | endCave = 0 | |
33 | ) | |
34 | ||
35 | func caveMask(cave uint16) uint16 { | |
36 | return 1 << cave | |
37 | } | |
38 | ||
39 | func (g *Graph) countPathsPart1(from uint16, visited uint16) int { | |
40 | var count int | |
41 | if count, ok := getCachedResult(from, visited); ok { | |
42 | return count | |
43 | } | |
44 | for _, to := range g.edges[from] { | |
45 | if to == endCave { | |
46 | count++ | |
47 | continue | |
48 | } | |
49 | toMask := caveMask(to) &^ g.bigCaves | |
50 | if visited&toMask == 0 { | |
51 | count += g.countPathsPart1(to, visited|toMask) | |
52 | } | |
53 | } | |
54 | putToCache(from, visited, count) | |
55 | return count | |
56 | } | |
57 | ||
58 | const doubleMask = 1 << 15 | |
59 | ||
60 | func (g *Graph) countPathsPart2(from uint16, visited uint16) int { | |
61 | var count int | |
62 | if count, ok := getCachedResult(from, visited); ok { | |
63 | return count | |
64 | } | |
65 | for _, to := range g.edges[from] { | |
66 | if to == endCave { | |
67 | count++ | |
68 | continue | |
69 | } | |
70 | toMask := caveMask(to) &^ g.bigCaves | |
71 | if visited&toMask == 0 { | |
72 | count += g.countPathsPart2(to, visited|toMask) | |
73 | } else if visited&doubleMask == 0 { | |
74 | count += g.countPathsPart2(to, visited|doubleMask) | |
75 | } | |
76 | } | |
77 | putToCache(from, visited, count) | |
78 | return count | |
79 | } | |
80 | ||
81 | // Memoïzation | |
82 | ||
83 | type key struct { | |
84 | cave uint16 | |
85 | visited uint16 | |
86 | } | |
87 | ||
88 | type cache map[key]int | |
89 | ||
90 | var memo cache | |
91 | ||
92 | func initCache() { | |
93 | - | memo = make(cache) |
93 | + | memo = make(cache, 500) |
94 | } | |
95 | ||
96 | func getCachedResult(from uint16, visited uint16) (int, bool) { | |
97 | count, ok := memo[key{from, visited}] | |
98 | return count, ok | |
99 | } | |
100 | ||
101 | func putToCache(from uint16, visited uint16, count int) { | |
102 | memo[key{from, visited}] = count | |
103 | } | |
104 | ||
105 | // Input parsing | |
106 | ||
107 | func NewGraph(input []string) *Graph { | |
108 | g := newBuilder() | |
109 | g.parse(input) | |
110 | return &g.Graph | |
111 | } | |
112 | ||
113 | func newBuilder() *graphBuilder { | |
114 | return &graphBuilder{ | |
115 | Graph: Graph{edges: make([][]uint16, 2, 16)}, | |
116 | caves: map[string]uint16{ | |
117 | "start": startCave, | |
118 | "end": endCave, | |
119 | }, | |
120 | } | |
121 | } | |
122 | ||
123 | type graphBuilder struct { | |
124 | Graph | |
125 | caves map[string]uint16 | |
126 | } | |
127 | ||
128 | func (g *graphBuilder) parse(input []string) { | |
129 | for _, line := range input { | |
130 | edge := strings.Split(line, "-") | |
131 | c1 := g.getCave(edge[0]) | |
132 | c2 := g.getCave(edge[1]) | |
133 | g.addEdge(c1, c2) | |
134 | } | |
135 | } | |
136 | ||
137 | func (g *graphBuilder) addEdge(c1, c2 uint16) { | |
138 | if c1 != endCave && c2 != startCave { | |
139 | g.edges[c1] = append(g.edges[c1], c2) | |
140 | } | |
141 | if c1 != startCave && c2 != endCave { | |
142 | g.edges[c2] = append(g.edges[c2], c1) | |
143 | } | |
144 | } | |
145 | ||
146 | func (g *graphBuilder) getCave(name string) uint16 { | |
147 | if cave, ok := g.caves[name]; ok { | |
148 | return cave | |
149 | } | |
150 | cave := uint16(len(g.caves)) | |
151 | g.caves[name] = cave | |
152 | - | if 'A' <= name[0] && name[0] < 'Z' { |
152 | + | if 'A' <= name[0] && name[0] <= 'Z' { |
153 | g.bigCaves |= caveMask(cave) | |
154 | } | |
155 | g.edges = append(g.edges, nil) | |
156 | return cave | |
157 | } | |
158 |