View difference between Paste ID: RiwyhHU8 and rnaDw4KM
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