-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathday22.go
188 lines (171 loc) · 4.51 KB
/
day22.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
package main
import (
"flag"
"fmt"
"io/ioutil"
"math/big"
"strconv"
"strings"
)
var inputFile = flag.String("inputFile", "inputs/day22.input", "Relative file path to use as input.")
type Deck [10007]int
type Operation func(pos, deckSize int64, iCache map[int64]int64) int64
func main() {
flag.Parse()
bytes, err := ioutil.ReadFile(*inputFile)
if err != nil {
return
}
var d Deck
for i := range d {
d[i] = i
}
ops := make([]Operation, 0)
// Part A
contents := string(bytes)
split := strings.Split(contents, "\n")
for _, s := range split {
if s == "" {
continue
}
tokens := strings.Split(s, " ")
var newDeck Deck
var op Operation
switch tokens[0] {
case "cut":
count, err := strconv.Atoi(tokens[1])
if err != nil {
fmt.Printf("Failed to parse cut count %s\n", tokens[1])
}
if count > 0 {
copy(newDeck[len(d)-count:], d[0:count])
copy(newDeck[0:len(d)-count], d[count:])
op = func(pos, deckSize int64, _ map[int64]int64) int64 {
if deckSize-int64(count) <= pos {
// We were shoved to the end, so we need to reverse.
// 0123456789 cut 4 ->
// 4567890123
return int64(count) - (deckSize - pos)
}
// We were shifted left, so we need to shift right.
return pos + int64(count)
}
} else {
copy(newDeck[0:-count], d[len(d)+count:])
copy(newDeck[-count:], d[0:len(d)+count])
op = func(pos, deckSize int64, _ map[int64]int64) int64 {
// count is negative.
if pos < -int64(count) {
// We were shoved to the front, so we need to reverse.
// 0123456789 cut -4 ->
// 6789012345
return deckSize + int64(count) + pos
}
// We were shifted right, so we need to shift left.
return pos + int64(count)
}
}
case "deal":
switch tokens[1] {
case "into":
for i, v := range d {
newDeck[len(d)-i-1] = v
}
op = func(pos, deckSize int64, _ map[int64]int64) int64 {
// 0123456789
// 9876543210
return deckSize - 1 - pos
}
case "with":
inc, err := strconv.Atoi(tokens[3])
if err != nil {
fmt.Printf("Failed to parse increment %s\n", tokens[4])
return
}
for i, v := range d {
newDeck[(inc*i)%len(d)] = v
}
op = func(pos, deckSize int64, iCache map[int64]int64) int64 {
// 0123456789
// 0741852963
// As long as count does not divide deckSize evenly (which we were promised),
// we can reverse division with modulus by multiplying by a different number
// and take the mod by deckSize as usual.
// In this example, N is 10, we've sorted by 3
// To reverse, take current position and multiply 7 and then modulus 10.
// N=11, sort by 4
// to reverse, multiply by 3 and then take modulus.
// 0123456789A
// 0369147A258
// the relation we're looking for is A*B === 1 (mod N)
if _, ok := iCache[int64(inc)]; !ok {
iCache[int64(inc)] = eea(int64(inc), deckSize)
}
var result big.Int
a := big.NewInt(iCache[int64(inc)])
b := big.NewInt(pos)
m := big.NewInt(deckSize)
result.Mul(a, b)
result.Mod(&result, m)
ret := result.Int64()
return ret
}
}
}
d = newDeck
ops = append(ops, op)
}
for i, v := range d {
if v == 2019 {
fmt.Println(i)
break
}
}
cCache := make(map[int64]int64)
for i, v := range d {
if r := reverse(int64(i), int64(10007), ops, cCache); r != int64(v) {
fmt.Printf("Failed to match value for position %d: %d != %d\n", i, v, r)
return
}
}
// Part B:
// Find the value of the card at position 2020 after 101741582076661 runs
// on a deck of length 119315717514047.
// This means we need to reverse the process for one run.
// Then reverse the reversal and so forth until we identify a pattern.
currentPos := int64(2020)
iCache := make(map[int64]int64)
fmt.Printf("Feed these values into Mathematica: 2020,")
for i := 1; i <= 3; i++ {
currentPos = reverse(currentPos, int64(119315717514047), ops, iCache)
fmt.Printf("%d,", currentPos)
}
fmt.Println()
}
func reverse(pos, deckSize int64, ops []Operation, iCache map[int64]int64) int64 {
reversedPos := pos
for i := len(ops) - 1; i >= 0; i-- {
f := ops[i]
reversedPos = f(reversedPos, deckSize, iCache)
}
return reversedPos
}
func eea(inc, deckSize int64) int64 {
ret := int64(0)
newT := int64(1)
r := deckSize
newR := inc
for newR != 0 {
q := r / newR
ret, newT = newT, ret-q*newT
r, newR = newR, r-q*newR
}
if r > 1 {
fmt.Println("Not invertible.")
return -1
}
if ret < 0 {
ret += deckSize
}
return ret
}