-
Notifications
You must be signed in to change notification settings - Fork 0
/
Solution_2021_18.swift
175 lines (144 loc) · 4.45 KB
/
Solution_2021_18.swift
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
//
// Solution_2021_18.swift
//
//
// Created by Ivan Chalov on 18.12.21.
//
import Foundation
struct Solution_2021_18: Solution {
var input: Input
func run() throws {
let input = try self.input.get()
.split(whereSeparator: \.isNewline)
.map(buildTree)
// ------- Part 1 -------
let part1 = input.scan { Node(left: $0, right: $1).reduced() }.magnitude
print(part1)
// ------- Part 2 -------
let part2 = input
.combinations(ofCount: 2)
.map {
max(
Node(left: $0[0], right: $0[1]).reduced().magnitude,
Node(left: $0[1], right: $0[0]).reduced().magnitude
)
}
.max()!
print(part2)
// ------- Test -------
assert(part1 == 4417, "WA")
assert(part2 == 4796, "WA")
}
}
private func buildTree<S: StringProtocol>(_ string: S) -> Node {
guard string.starts(with: "[") else {
return Node(element: Int(string)!)
}
let s = string.dropFirst().dropLast()
var parensBalance = 0
var separatorIndex = s.startIndex
for i in s.indices {
if s[i] == "[" { parensBalance += 1 }
else if s[i] == "]" { parensBalance -= 1 }
else if s[i] == ",", parensBalance == 0 { separatorIndex = i; break }
}
return Node(
left: buildTree(s[..<separatorIndex]),
right: buildTree(s[s.index(after: separatorIndex)...])
)
}
private class Node: CustomStringConvertible {
var element: Int?
var left: Node?
var right: Node?
weak var parent: Node?
init(element: Int? = nil, left: Node? = nil, right: Node? = nil, parent: Node? = nil) {
self.element = element
self.left = left
self.right = right
self.parent = parent
self.left?.parent = self
self.right?.parent = self
}
var description: String {
if let element = element {
return String(element)
} else if let left = left, let right = right {
return "[\(String(describing: left)),\(String(describing: right))]"
}
preconditionFailure("invalid tree")
}
func reduced() -> Node {
let newNode = self.copy()
func reduce1() -> Bool {
return newNode.explode() ? true : newNode.split()
}
while reduce1() { }
return newNode
}
private func explode(_ depth: Int = 0) -> Bool {
guard element == nil else { return false }
guard depth < 5 else { preconditionFailure("should never reach this depth") }
if depth == 4 {
var search: Node? = self
var newSearch = search
while (newSearch = search?.parent, newSearch).1?.left === search {
search = newSearch
}
search = newSearch?.left
while search?.right != nil {
search = search?.right
}
if let search = search {
search.element! += left!.element!
}
search = self
newSearch = search
while (newSearch = search?.parent, newSearch).1?.right === search {
search = newSearch
}
search = newSearch?.right
while search?.left != nil {
search = search?.left
}
if let search = search {
search.element! += right!.element!
}
left = nil
right = nil
element = 0
return true
}
return left?.explode(depth + 1) == true
? true
: right?.explode(depth + 1) == true
}
private func split() -> Bool {
if let element = element, element > 9 {
self.element = nil
left = Node(element: element / 2, parent: self)
right = Node(element: (element + 1) / 2, parent: self)
return true
} else {
return left?.split() == true
? true
: right?.split() == true
}
}
var magnitude: Int {
if let element = element {
return element
}
if let left = left, let right = right {
return 3 * left.magnitude + 2 * right.magnitude
}
preconditionFailure("invalid tree")
}
func copy() -> Node {
Node(
element: element,
left: left?.copy(),
right: right?.copy()
)
}
}