-
Notifications
You must be signed in to change notification settings - Fork 3
/
day16.go
258 lines (224 loc) · 6.2 KB
/
day16.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// Package main provides ...
package main
import (
"fmt"
"io/ioutil"
"log"
"strconv"
"strings"
)
type Instruction int
const (
Spin Instruction = 1
SwapIndex Instruction = 2
SwapChar Instruction = 3
)
type Command struct {
Inst Instruction
SpinAmount int
SwapIndex0 int
SwapIndex1 int
SwapChar0 string
SwapChar1 string
}
func Parse(filename string) []Command {
commands := make([]Command, 0)
b, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
}
s := string(b)
sList := strings.Split(strings.TrimSpace(s), ",")
for _, sCommand := range sList {
// sInt (examples) "x7/5", "x13/4", "s14", "pc/m"
first, rest := sCommand[0], sCommand[1:]
command := Command{}
if int(first) == int('s') {
command = ParseSpin(rest)
} else if int(first) == int('x') {
command = ParseExchange(rest)
} else if int(first) == int('p') {
command = ParsePartner(rest)
}
commands = append(commands, command)
}
return commands
}
func ParseSpin(rest string) Command {
// parse "s14"
num, err := strconv.Atoi(rest)
if err != nil {
log.Fatal(err)
}
return Command{Inst: Spin, SpinAmount: num}
}
func ParseExchange(rest string) Command {
// parse "x7/5"
nums := strings.Split(rest, "/")
num0, err := strconv.Atoi(nums[0])
if err != nil {
log.Fatal(err)
}
num1, err := strconv.Atoi(nums[1])
if err != nil {
log.Fatal(err)
}
return Command{Inst: SwapIndex, SwapIndex0: num0, SwapIndex1: num1}
}
func ParsePartner(rest string) Command {
// parse "pc/m"
chars := strings.Split(rest, "/")
char0 := chars[0]
char1 := chars[1]
return Command{Inst: SwapChar, SwapChar0: char0, SwapChar1: char1}
}
/// PARSING END
func Rotate(nums []int, k int) []int {
if k < 0 || len(nums) == 0 {
return nums
}
r := len(nums) - k%len(nums)
nums = append(nums[r:], nums[:r]...)
return nums
}
func Init(size int) ([]int, map[string]string) {
// If size 5, Start with
// Positions = [0, 1, 2, 3, 4]
// Swaps = ["a": "a", "b": "b", ... "e": "e"]
positions := make([]int, size)
swaps := make(map[string]string, size)
for index := 0; index < size; index++ {
positions[index] = index
letter := string(rune(97 + index))
swaps[letter] = letter
}
return positions, swaps
}
func ApplyCommandsMultiple(times int, positions_in []int, swaps_in map[string]string, commands []Command) ([]int, map[string]string) {
// Copy positions_in -> positions
positions := make([]int, len(positions_in))
copy(positions, positions_in)
// Copy swaps_in -> swaps
swaps := make(map[string]string, len(swaps_in))
for key, value := range swaps_in {
swaps[key] = value
}
positions_for_step := make(map[int][]int, 0)
swaps_for_step := make(map[int]map[string]string, 0)
i := 1
positions_for_step[1] = positions
swaps_for_step[1] = swaps
for i < times {
// fmt.Println(i)
if i*2 < times {
// Able to double
// fmt.Println("Double")
positions, swaps = Compose(positions, swaps, positions, swaps)
i *= 2
} else {
// Can't double, find the largest number we've already computed to add
gap := times - i
next_step := 1
for k, _ := range positions_for_step {
if k > next_step && k <= gap {
next_step = k
}
}
// fmt.Printf("--> %v\n", next_step)
positions, swaps = Compose(positions, swaps, positions_for_step[next_step], swaps_for_step[next_step])
i += next_step
}
positions_for_step[i] = positions
swaps_for_step[i] = swaps
}
return positions, swaps
}
func Compose(positions_in1 []int, swaps_in1 map[string]string, positions_in2 []int, swaps_in2 map[string]string) ([]int, map[string]string) {
positions := make([]int, len(positions_in1))
swaps := make(map[string]string, len(swaps_in1))
// Positions 1 [5, 4, 3, 2, 1, 0]
// Positions 2 [0, 2, 1, 3, 4, 5]
// Compose these. Use the values in 2 as indexes to look up 1
for i, _ := range positions_in1 {
val2 := positions_in2[i]
positions[i] = positions_in1[val2]
}
// Swaps 1 { "a": "a", "b": "c", "c": "b", "d": "d" }
// Swaps 2 { "a": "a", "b": "b", "c": "d", "d": "c" }
for key1, value1 := range swaps_in1 {
swaps[key1] = swaps_in2[value1]
}
return positions, swaps
}
func ApplyCommands(positions_in []int, swaps_in map[string]string, commands []Command) ([]int, map[string]string) {
// Copy positions_in -> positions
positions := make([]int, len(positions_in))
copy(positions, positions_in)
// Copy swaps_in -> swaps
swaps := make(map[string]string, len(swaps_in))
for key, value := range swaps_in {
swaps[key] = value
}
for _, command := range commands {
if command.Inst == Spin {
positions = Rotate(positions, command.SpinAmount)
} else if command.Inst == SwapIndex {
i := command.SwapIndex0
j := command.SwapIndex1
positions[i], positions[j] = positions[j], positions[i]
} else if command.Inst == SwapChar {
i := ""
j := ""
for key, value := range swaps {
if value == command.SwapChar0 {
i = key
}
if value == command.SwapChar1 {
j = key
}
}
swaps[i], swaps[j] = swaps[j], swaps[i]
}
}
return positions, swaps
}
func FinalPositions(length int, positions []int, swaps map[string]string) string {
letters := make([]string, length)
for index := 0; index < length; index++ {
letter := string(rune(97 + index))
letters[index] = letter
}
final := make([]string, length)
for i, pos := range positions {
final[i] = swaps[letters[pos]]
}
return strings.Join(final[:], "")
}
func Part1(length int, commands []Command) string {
positions, swaps := Init(length)
positions, swaps = ApplyCommands(positions, swaps, commands)
return FinalPositions(length, positions, swaps)
}
func Part2(length int, commands []Command) string {
positions, swaps := Init(length)
positions, swaps = ApplyCommands(positions, swaps, commands)
positions, swaps = ApplyCommandsMultiple(1_000_000_000, positions, swaps, commands)
return FinalPositions(length, positions, swaps)
}
func main() {
// Test
commands := Parse("../input_small.txt")
final := Part1(5, commands)
fmt.Println("Part 1 Example:")
fmt.Println(final)
fmt.Println("Part 2 Example:")
fmt.Println(Part2(5, commands))
fmt.Println("")
// Part 1
commands = Parse("../input.txt")
final = Part1(16, commands)
fmt.Println("Part 1:")
fmt.Println(final)
fmt.Println("Part 2:")
fmt.Println(Part2(16, commands))
}