This repository has been archived by the owner on Apr 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
153 lines (134 loc) · 2.99 KB
/
main.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
package main
import (
"fmt"
"github.com/derat/advent-of-code/lib"
)
func main() {
pw := lib.InputLinesBytes("2015/11")[0]
charNum := make(map[byte]byte)
numChar := make(map[byte]byte)
var i, max byte
for ch := byte('a'); ch <= 'z'; ch++ {
if ch == 'i' || ch == 'l' || ch == 'o' {
continue
}
charNum[ch] = i
numChar[i] = ch
max = i
i++
}
// Remap to [0, ...].
for i, ch := range pw {
n, ok := charNum[ch]
lib.Assert(ok) // input doesn't seem to have invalid chars
pw[i] = n
}
// Remap back to the original chars.
remap := func(pw []byte) string {
b := make([]byte, len(pw))
for i, ch := range pw {
b[i] = numChar[ch]
}
return string(b)
}
// Part 1
findNext(pw, max)
fmt.Println(remap(pw))
// Part 2
findNext(pw, max)
fmt.Println(remap(pw))
}
func findNext(pw []byte, max byte) {
// Perform a simple increment first.
increment(pw, max)
// Now loop until we get both a straight and pairs.
for {
if !hasStraight(pw) {
addStraight(pw, max)
continue
}
if !hasPairs(pw) {
for !hasPairs(pw) {
// This is stupid, but I had enough trouble getting addStraight()
// to work and don't want to do the same thing with an addPairs()
// function.
increment(pw, max)
}
continue
}
return
}
}
// increment performs a simple increment of pw.
func increment(pw []byte, max byte) {
for i := len(pw) - 1; i >= 0; i-- {
if pw[i] < max {
pw[i]++
return
}
pw[i] = 0 // carry
}
panic("Overflow on increment")
}
// hasStraight returns true if pw contains a 3-char ascending straight, e.g. "abc".
func hasStraight(pw []byte) bool {
for i, ch := range pw[:len(pw)-2] {
if pw[i+1] == ch+1 && pw[i+2] == ch+2 {
return true
}
}
return false
}
// addStraight inserts an ascending straight in the first possible position.
func addStraight(pw []byte, max byte) {
for {
// Setting the first or second char in the loop could've produced a straight
// further to the left.
if hasStraight(pw) {
return
}
// Consider sequences of three characters starting at the right.
for i := len(pw) - 3; i >= 0; i-- {
ch, ch1, ch2 := pw[i], pw[i+1], pw[i+2]
// Try to set the third char to make the straight.
if ch1 == ch+1 && ch2 < ch+2 && ch < max-1 {
pw[i+2] = ch + 2
for j := i + 3; j < len(pw); j++ {
pw[j] = 0
}
return
}
// Set the second char.
if ch1 < ch+1 && ch < max {
pw[i+1] = ch + 1
for j := i + 2; j < len(pw); j++ {
pw[j] = 0
}
break
}
// Increment the first char.
if ch1 >= ch+1 && ch < max {
pw[i] = ch + 1
for j := i + 1; j < len(pw); j++ {
pw[j] = 0
}
break
}
// Otherwise, we'll move on to the left.
}
}
}
// hasPairs returns true if pw contains at least two different, non-overlapping pairs of chars,
// e.g. "aa" and "zz".
func hasPairs(pw []byte) bool {
pairs := make(map[byte]struct{})
for i, ch := range pw[:len(pw)-1] {
if pw[i+1] == ch {
pairs[ch] = struct{}{}
if len(pairs) >= 2 {
return true
}
}
}
return false
}