-
Notifications
You must be signed in to change notification settings - Fork 0
/
Day18.kt
161 lines (122 loc) · 4.78 KB
/
Day18.kt
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
import java.io.InputStream
private sealed class SnailFishNumber {
abstract var parent: SnailFishPair?
abstract fun magnitude(): Int
abstract fun deepCopy(): SnailFishNumber
operator fun plus(other: SnailFishNumber): SnailFishNumber {
val result = SnailFishPair(this, other)
result.reduce()
return result
}
protected tailrec fun reduce() {
if (explode() || split()) reduce()
}
private fun explode(): Boolean {
val found = findFourth() ?: return false
found.findLeftNumber()?.run { number += (found.left as SnailFishLiteral).number }
found.findRightNumber()?.run { number += (found.right as SnailFishLiteral).number }
val foundParent = found.parent!!
val new = SnailFishLiteral(0, foundParent)
new.parent = foundParent
if (foundParent.left === found) {
foundParent.left = new
} else {
foundParent.right = new
}
return true
}
fun split(): Boolean {
val found = findLeftGreater10() ?: return false
val foundParent = found.parent!!
val new = SnailFishPair(
SnailFishLiteral(found.number / 2),
SnailFishLiteral(found.number / 2 + (found.number and 1))
)
new.parent = foundParent
if (foundParent.left === found) {
foundParent.left = new
} else {
foundParent.right = new
}
return true
}
private fun findFourth(depth: Int = 0): SnailFishPair? {
if (depth == 4) return this as? SnailFishPair
return when (val found = this) {
is SnailFishPair -> found.left.findFourth(depth + 1) ?: found.right.findFourth(depth + 1)
is SnailFishLiteral -> null
}
}
protected fun findLeftNumber(): SnailFishLiteral? {
val currentParent = parent ?: return null
return if (currentParent.right === this) currentParent.left.diveRight() else currentParent.findLeftNumber()
}
protected fun findRightNumber(): SnailFishLiteral? {
val currentParent = parent ?: return null
return if (currentParent.left === this) currentParent.right.diveLeft() else currentParent.findRightNumber()
}
private fun findLeftGreater10(): SnailFishLiteral? = when (this) {
is SnailFishLiteral -> if (number > 9) this else null
is SnailFishPair -> left.findLeftGreater10() ?: right.findLeftGreater10()
}
protected abstract fun diveLeft(): SnailFishLiteral
protected abstract fun diveRight(): SnailFishLiteral
companion object {
fun parse(inputStream: InputStream): SnailFishNumber? = when (val read = inputStream.read()) {
-1 -> null
'['.code -> {
val left = parse(inputStream).expect()
check(inputStream.read() == ','.code)
val right = parse(inputStream).expect()
check(inputStream.read() == ']'.code)
SnailFishPair(left, right)
}
in '0'.code..'9'.code -> SnailFishLiteral(read.toChar().toString().toInt())
else -> error("Invalid number")
}
fun parse(string: String) = parse(string.byteInputStream())
}
}
private data class SnailFishPair(
var left: SnailFishNumber,
var right: SnailFishNumber,
override var parent: SnailFishPair? = null,
) : SnailFishNumber() {
init {
left.parent = this
right.parent = this
}
override fun magnitude() = 3 * left.magnitude() + 2 * right.magnitude()
override fun deepCopy() = SnailFishPair(left.deepCopy(), right.deepCopy())
override fun toString() = "[$left,$right]"
override fun diveLeft(): SnailFishLiteral = when (val left = left) {
is SnailFishLiteral -> left
is SnailFishPair -> left.diveLeft()
}
override fun diveRight(): SnailFishLiteral = when (val right = right) {
is SnailFishLiteral -> right
is SnailFishPair -> right.diveRight()
}
}
private data class SnailFishLiteral(var number: Int, override var parent: SnailFishPair? = null) : SnailFishNumber() {
override fun magnitude() = number
override fun deepCopy() = SnailFishLiteral(number)
override fun toString() = "$number"
override fun diveLeft() = this
override fun diveRight() = this
}
private fun part1(numbers: List<SnailFishNumber>) =
numbers
.asSequence()
.map { it.deepCopy() }
.reduce(SnailFishNumber::plus)
.magnitude()
private fun part2(numbers: List<SnailFishNumber>) =
(numbers cartesian numbers)
.filter { (a, b) -> a !== b }
.maxOfOrNull { (a, b) -> (a.deepCopy() + b.deepCopy()).magnitude() }
fun main() {
val numbers = mapLines { SnailFishNumber.parse(it).expect() }
println(part1(numbers))
println(part2(numbers))
}