-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday12.go
165 lines (141 loc) · 4.67 KB
/
day12.go
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package main
import (
"fmt"
"strings"
"github.com/Alex-Waring/AoC/utils"
)
type GearSet struct {
damaged_springs []string
input string
}
type Solver struct {
cache map[string]int
}
func createCacheString(input string, damaged_springs []string) string {
key := input
for _, spring := range damaged_springs {
key += ","
key += spring
}
return key
}
func (s *Solver) solve(input string, damaged_springs []string, index int) int {
cache_key := createCacheString(input, damaged_springs)
if v, ok := s.cache[cache_key]; ok {
return v
}
// Debug blocks
// if index > 17 {
// fmt.Print("Solving " + input + " for ")
// fmt.Println(damaged_springs)
// }
// If there is no input left, return 1 if groups are empty, or return 0
// This is the catch block when we have finished solving
if len(input) == 0 {
if len(damaged_springs) == 0 {
s.cache[cache_key] = 1
// No input and no groups means a win!
return 1
} else {
s.cache[cache_key] = 0
// We have no input but there are groups left, fail
return 0
}
}
// If the length of the input is less than the sum of the groups, it's not possible
if len(input) < utils.SumListString(damaged_springs) {
return 0
}
// If it starts with a ., strip that dot
if strings.HasPrefix(input, ".") {
result := s.solve(strings.TrimPrefix(input, "."), damaged_springs, index)
s.cache[cache_key] = result
return result
}
// If it starts with a ? then try both . and #
if strings.HasPrefix(input, "?") {
// This is the branch
result := s.solve(strings.Replace(input, "?", "#", 1), damaged_springs, index) + s.solve(strings.Replace(input, "?", ".", 1), damaged_springs, index)
s.cache[cache_key] = result
return result
}
// Start finding the gear set
if strings.HasPrefix(input, "#") {
// Shouldn't really be here, returning zero for cases where it's not a possible set
// Option 1 is we have no more groups left, we know we have a gear left in the input so this is a fail
// Option 2 is if the length of the input is less than the first group, we shouldn't hit this because
// of line 56 but it's a good catch
if (len(damaged_springs) == 0) || (len(input) < utils.IntegerOf(damaged_springs[0])) {
s.cache[cache_key] = 0
return 0
}
// If there isn't enough room at the start for the first gear, return zero
if strings.Contains(input[0:utils.IntegerOf(damaged_springs[0])], ".") {
s.cache[cache_key] = 0
return 0
}
// If we have more than one set left, solve the first set
if len(damaged_springs) > 1 {
// Catch if there wouldn't be enough space left for another set
if len(input) < utils.IntegerOf(damaged_springs[0])+1 {
s.cache[cache_key] = 0
return 0
}
// Catch if the group we are tring to match would end in a # (there are no overlaps)
if input[utils.IntegerOf(damaged_springs[0])] == '#' {
s.cache[cache_key] = 0
return 0
}
// Remove the set we matched and the first group and keep solving
result := s.solve(input[utils.IntegerOf(damaged_springs[0])+1:], damaged_springs[1:], index)
s.cache[cache_key] = result
return result
} else {
// We have one group left, remove it and solve again
result := s.solve(input[utils.IntegerOf(damaged_springs[0]):], damaged_springs[1:], index)
s.cache[cache_key] = result
return result
}
}
panic("Should not be here")
}
func part1(spring_list []GearSet) {
defer utils.Timer("part1")()
total_arrangements := 0
s := Solver{make(map[string]int)}
for index, gearSet := range spring_list {
total_arrangements += s.solve(gearSet.input, gearSet.damaged_springs, index)
}
fmt.Println(total_arrangements)
}
func part2(spring_list []GearSet) {
defer utils.Timer("part2")()
unfolded_spring_list := []GearSet{}
for _, set := range spring_list {
// At this point this is just easier
new_input := set.input + "?" + set.input + "?" + set.input + "?" + set.input + "?" + set.input
new_damaged_springs := set.damaged_springs
for i := 0; i <= 3; i++ {
new_damaged_springs = append(new_damaged_springs, set.damaged_springs...)
}
unfolded_spring_list = append(unfolded_spring_list, GearSet{input: new_input, damaged_springs: new_damaged_springs})
}
total_arrangements := 0
s := Solver{make(map[string]int)}
for index, gearSet := range unfolded_spring_list {
total_arrangements += s.solve(gearSet.input, gearSet.damaged_springs, index)
}
fmt.Println(total_arrangements)
}
func main() {
input := utils.ReadInput("input.txt")
spring_list := []GearSet{}
for _, line := range input {
set := GearSet{
input: strings.Split(line, " ")[0], damaged_springs: strings.Split(strings.Split(line, " ")[1], ","),
}
spring_list = append(spring_list, set)
}
part1(spring_list)
part2(spring_list)
}